Quickstart
#Quickstart
#1. Get credentials from your club admin
Only a club admin can create API credentials, in the panel under Settings → API access. Ask them to:
- Open Settings → API access and click Create client.
- Give it a name (e.g. "Club website"), pick scopes (
events:read,results:read), optionally restrict caller IPs, accept the API Terms the first time. - Copy the client ID and client secret shown on the confirmation screen — the secret is shown once and cannot be retrieved again (only rotated, which invalidates the old one).
Store both as environment variables (SAIL_CLUB_CLIENT_ID, SAIL_CLUB_CLIENT_SECRET). Never commit them,
never send them to a browser.
#2. Get a token
Prompt for AI agents ready to paste into an agent
Exchange a Sail Club API client_id/client_secret for a bearer token: POST
https://sail-club-server.cloud.run/api/v1/oauth/token as application/x-www-form-urlencoded, with
grant_type=client_credentials and the client_id/client_secret sent as HTTP Basic auth. Cache the returned
access_token in memory for its expires_in seconds; do not fetch a new one per call.#Request
curl -s -X POST https://sail-club-server.cloud.run/api/v1/oauth/token \
-u "$SAIL_CLUB_CLIENT_ID:$SAIL_CLUB_CLIENT_SECRET" \
-d grant_type=client_credentialsconst creds = Buffer.from(`${process.env.SAIL_CLUB_CLIENT_ID}:${process.env.SAIL_CLUB_CLIENT_SECRET}`).toString('base64');
const res = await fetch('https://sail-club-server.cloud.run/api/v1/oauth/token', {
method: 'POST',
headers: { Authorization: `Basic ${creds}`, 'Content-Type': 'application/x-www-form-urlencoded' },
body: 'grant_type=client_credentials',
});
const { access_token, expires_in } = await res.json();import os, requests
res = requests.post(
"https://sail-club-server.cloud.run/api/v1/oauth/token",
auth=(os.environ["SAIL_CLUB_CLIENT_ID"], os.environ["SAIL_CLUB_CLIENT_SECRET"]),
data={"grant_type": "client_credentials"},
)
token = res.json()["access_token"]using var client = new HttpClient { BaseAddress = new Uri("https://sail-club-server.cloud.run") };
var clientId = Environment.GetEnvironmentVariable("SAIL_CLUB_CLIENT_ID");
var clientSecret = Environment.GetEnvironmentVariable("SAIL_CLUB_CLIENT_SECRET");
var authHeader = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{clientId}:{clientSecret}"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", authHeader);
var res = await client.PostAsync("/api/v1/oauth/token",
new FormUrlEncodedContent(new Dictionary<string, string> { ["grant_type"] = "client_credentials" }));
var body = await res.Content.ReadFromJsonAsync<JsonElement>();
var token = body.GetProperty("access_token").GetString();<?php
$clientId = getenv('SAIL_CLUB_CLIENT_ID');
$clientSecret = getenv('SAIL_CLUB_CLIENT_SECRET');
$ch = curl_init('https://sail-club-server.cloud.run/api/v1/oauth/token');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_USERPWD => "$clientId:$clientSecret",
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => 'grant_type=client_credentials',
]);
$token = json_decode(curl_exec($ch), true)['access_token'];Full reference: POST /api/v1/oauth/token.
#3. Make your first call
curl -s https://sail-club-server.cloud.run/api/v1/events \
-H "Authorization: Bearer $TOKEN"You'll get back the club's published events, the same list its public results pages show — see List events for the full shape.
#4. Next
- Authentication — token lifetime, refresh pattern, secret handling.
- Caching & freshness — don't poll harder than the data actually changes.
- Errors — what every
codemeans and how to react to it. - Build with AI — hand a coding agent the full-integration prompt.