Skip to main content

Leagues

A league (an event series in the API) is a season a golfer registers and pays for once. Every event linked to it draws its field from the league roster, including golfers who join after those events were created — so a Thursday-night league is set up in one place rather than event by event.

Everything on this page is public and unauthenticated. Managing a league — its roster, its teams, its linked events — is an admin API and is not part of the public surface.

List an organization's leagues​

curl "https://api.front9.com/api/public/v1/orgs/{orgSlug}/event-series"

Returns leagues that are open, active or completed. Draft and private leagues are omitted; members-only leagues appear (registration is gated separately, the same way events behave).

[
{
"id": 7,
"name": "Thursday Night Simulator Scramble",
"slug": "thursday-night-simulator-scramble",
"seasonLabel": "2026 season",
"scheduleLabel": "Thursdays: 7:00-9:30",
"status": "active",
"feeMembersCents": 12000,
"feePerEntry": true,
"entrySize": 2,
"seriesImageUrl": "/media/defaults/event-series/league-1.png",
"memberCount": 20,
"linkedEventCount": 4
}
]

Two fields shape everything else:

  • entrySize — 1 is an individual league. 2 or more makes it a team league: golfers register as a declared pair (or foursome) that stays together all season, and the teams below apply.
  • feePerEntry — when true the fee buys a whole entry, so a pair pays once between them and the partner joins free. When false every golfer pays.

memberCount is the active roster size and linkedEventCount how many events the league feeds, so a listing page needs no second round trip. All prices are in cents.

scheduleLabel is free text for when the league plays, written by the organizer — "Thursdays: 7:00-9:30". Render it as given: it is never parsed, may say anything ("alternate Sundays, weather permitting"), and may be absent. The authoritative dates are on the linked events; this is the one-line answer to "when is this league?" for a listing or a header.

Filtering​

The list takes three optional query parameters, combined with AND:

ParameterMeaning
statusopen, active or completed. Repeat it or comma-separate (status=open,active). Any other value is a 400.
yearLeagues whose season overlaps that calendar year. A November–February league appears under both years. A league with no dates never matches.
qCase-insensitive search on name, season label and description.
curl "https://api.front9.com/api/public/v1/orgs/{orgSlug}/event-series?year=2026&status=open,active&q=couples"

To build a year picker, ask for the years that have leagues:

curl "https://api.front9.com/api/public/v1/orgs/{orgSlug}/event-series/list-metadata"
{
"years": [2027, 2026, 2025],
"oldestYear": 2025,
"newestYear": 2027,
"statusCounts": { "open": 2, "active": 1, "completed": 4 }
}

years is newest first, and each year in it returns at least one league from ?year=. statusCounts omits statuses with no leagues.

Get a single league​

curl "https://api.front9.com/api/public/v1/orgs/{orgSlug}/event-series/{slug}"

Same shape, addressed by slug — this is the league's public landing page. Returns 404 for a league that is private or still in draft, so a not-found and a not-public league are indistinguishable from outside.

A league's schedule and standings​

A league page usually knows one thing: the league's slug, out of its own URL. Both of the things such a page wants to show are addressable that way, so it never has to learn an internal id.

The nights, from the ordinary event list:

curl "https://api.front9.com/api/public/v1/orgs/{orgSlug}/events?filter=all&eventSeries={slug}"

eventSeries matches on the link between an event and its league — the one an admin makes when adding a night — not on a tag or a naming convention, so the schedule cannot drift from the league itself. It combines with the other filters (tag, filter, year), and a slug that names no public league is a 404.

The table:

curl "https://api.front9.com/api/public/v1/orgs/{orgSlug}/event-series/{slug}/standings"
{
"standingSeries": { "id": 5, "name": "Mixed 9-Team League", "scope": "team", "seasons": [] },
"table": { "seriesId": 5, "seriesName": "Mixed 9-Team League", "rows": [] }
}

The league carries the link to its standings table (standingSeriesId), so this resolves it for you. The series comes back alongside the table because a season-based league needs its seasons to offer a season picker, and because standingSeries.id is what every follow-up request uses — a different season (?seasonId=), or a competitor's contributions — through the ordinary standings endpoints.

A league with no standings table linked answers 200 with both fields null. The league is real, it simply hasn't been wired up, and a page can say so rather than show an error. Set standingSeriesId on the league to connect the two.

Start a registration​

As with events and memberships, payment never flows through this API. The start endpoint hands you a hosted URL where Front9 collects the card.

curl -X POST \
"https://api.front9.com/api/public/v1/orgs/{orgSlug}/event-series/{id}/register/start"
{ "url": "https://registration.front9.com/?org=acme&eventSeries=7" }

Redirect the golfer to url to finish registering and pay for the season.

On a team league, pass ?eventSeriesTeam={teamId} to land the golfer on an existing team rather than starting a new one — this is what a partner following an invite link needs:

curl -X POST \
"https://api.front9.com/api/public/v1/orgs/{orgSlug}/event-series/{id}/register/start?eventSeriesTeam=12"

The parameter is passed straight through to the hosted page, so forward org, eventSeries and eventSeriesTeam through your signup flow if the golfer has to create an account on the way.

On a team league, a registrant names their partners and each is emailed a join link. Before that visitor has an account, you can read what they are joining:

curl "https://api.front9.com/api/public/v1/orgs/{orgSlug}/event-series/{id}/teams/{teamID}"
{
"id": 12,
"name": "Wolfpack",
"memberNames": ["Jamie Lee"],
"entrySize": 4,
"seatsFilled": 1,
"seatsRemaining": 3,
"complete": false
}

Deliberately narrow: names and counts only — never user ids, never the invited email addresses. It is served to whoever holds the link.

Use it to show what the visitor is joining and how many seats are open. The seats they can fill for other people is seatsRemaining - 1 — their own seat comes out of the same total.

Partner invites​

When the invited golfer already has a Front9 account, their emailed link carries an inviteToken. Resolve it to render the join page:

curl "https://api.front9.com/api/public/v1/event-series-invites/{token}"
{
"eventSeries": {
"id": 7,
"slug": "thursday-night-simulator-scramble",
"name": "Thursday Night Simulator Scramble",
"orgSlug": "acme",
"orgName": "Acme GC"
},
"team": {
"id": 12,
"name": "Wolfpack",
"memberNames": ["Jamie Lee"],
"entrySize": 4,
"seatsFilled": 1,
"seatsRemaining": 3,
"complete": false
},
"inviter": { "firstName": "Jamie", "lastName": "Lee" },
"email": "partner@example.com",
"feeCents": 12000,
"accepted": false
}
  • feeCents — what this golfer owes. Unlike an event invite nobody else is covering it: a league partner pays their own season fee. The exception is a feePerEntry league where a teammate has already bought the entry, in which case it comes back 0.
  • accepted — the link outlives the invite. Once the golfer registers it keeps resolving with accepted: true, so a second click can say "you're already registered" instead of erroring. It stops resolving only if the team is deleted.
  • team — absent if the team has since been deleted.

An invited address with no Front9 account gets no token; their link carries only org, eventSeries and eventSeriesTeam, and the team endpoint above is all you need to render it.

404 if the token is unknown.

Sub requests​

When a regular on a team league can't make a night, the organizer can post their seat as a sub request: one night, one seat, optionally with a fee the substitute pays, and open to everyone, to the organization's members, or to golfers already playing in one of its leagues. Your site lists the open requests, and a golfer who takes one plays that night in the regular's seat — their result counts for the regular's team.

List every open request across the organization (the "subs needed" page):

curl "https://api.front9.com/api/public/v1/orgs/{orgSlug}/sub-requests"

or just one league's:

curl "https://api.front9.com/api/public/v1/orgs/{orgSlug}/event-series/{slug}/sub-requests"
[
{
"id": 7,
"eventSeriesId": 3,
"eventSeriesName": "Thursday Couples",
"eventSeriesSlug": "thursday-couples",
"teamName": "Wolfpack",
"eventId": 112,
"eventTitle": "Week 4",
"eventSlug": "week-4",
"eventDate": "2026-11-05",
"feeCents": 2000,
"eligibility": "everyone",
"membersOnly": false
}
]
  • feeCents — what the substitute pays. 0 means free.

  • eligibility — who can take it:

    • everyone;
    • org_members — golfers with an active membership in the organization;
    • league_players — golfers on the roster of one of the organization's leagues that is open or in progress (any league, not only this one).

    Show it so golfers who don't qualify aren't sent into a flow that will turn them away. membersOnly is the older form of the same information: true exactly when eligibility is org_members.

  • eventStartTime — the night's first tee time, present once the tee sheet has one. teamName and note are also omitted when empty.

The regular who is out is never named — the list is public. Only open requests appear, on leagues that are open or active and not private, for nights that haven't started. A request drops off the list as soon as someone takes it or the organizer withdraws it.

To render a page for one request:

curl "https://api.front9.com/api/public/v1/orgs/{orgSlug}/sub-requests/{requestID}"

Same shape. It answers 404 once the request is no longer listed, so a stale link can say "this spot has been filled" rather than offer a dead button.

Taking a sub request​

Taking a request goes through the same hosted registration page as joining the league. Pass the request's id as subRequest to the league's start endpoint:

curl -X POST \
"https://api.front9.com/api/public/v1/orgs/{orgSlug}/event-series/{eventSeriesId}/register/start?subRequest=7"
{ "url": "https://registration.front9.com/?org=acme&eventSeries=3&subRequest=7" }

Redirect the golfer to url. Front9 signs them in, checks they're eligible, collects the fee if there is one and puts them in the seat. Only one golfer can take a given request: if two try at once, the second is told it has been filled before their card is charged. As with the other parameters, forward subRequest through your signup flow if the golfer has to create an account on the way.

Leagues feed the ordinary event endpoints — a league night is a normal event, so its field, tee sheet, leaderboard and live streams are the same endpoints as any other. See Real-time updates and the API reference.