Quick Start

From zero to your first API call in 5 minutes. You need a client_id and a private key (created in the Admin UI).

🔑

1. Get Credentials

Register an API client in Admin. Download the private key PEM file.

📝

2. Sign Assertion

Create a JWT signed with your private key (ES256).

🎫

3. Get Token

POST to /auth/token to exchange for an access token.

🚀

4. Call API

Use Authorization: Bearer <token> on all requests.

Python

# pip install authlib requests

from authlib.jose import jwt as jose_jwt
from datetime import datetime, timedelta, UTC
import uuid, requests

# Load your private key (received when client was created in Admin)
with open("client-private-key.pem", "rb") as f:
    private_key = f.read()

client_id = "acme-integration"
token_endpoint = "http://api.petanque.life/auth/token"

# Sign a client assertion
now = datetime.now(UTC)
assertion = jose_jwt.encode(
    header={"alg": "ES256", "kid": "2026-04-primary"},
    payload={
        "iss": client_id,
        "sub": client_id,
        "aud": token_endpoint,
        "jti": str(uuid.uuid4()),
        "iat": int(now.timestamp()),
        "exp": int((now + timedelta(minutes=2)).timestamp()),
    },
    key=private_key,
).decode()

# Request an access token (scope = capability names)
resp = requests.post(token_endpoint, data={
    "grant_type": "client_credentials",
    "client_id": client_id,
    "client_assertion_type": "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
    "client_assertion": assertion,
    "scope": "bookings:read",
})
resp.raise_for_status()
access_token = resp.json()["access_token"]

# Call the API
bookings = requests.get(
    "http://api.petanque.life/bookings",
    headers={"Authorization": f"Bearer {access_token}"},
).json()
print(f"Found {bookings['total']} bookings")

C#

// NuGet: Microsoft.IdentityModel.Tokens, System.IdentityModel.Tokens.Jwt
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Security.Cryptography;

var clientId = "acme-integration";
var tokenEndpoint = "http://api.petanque.life/auth/token";

// Load private key
var ecdsa = ECDsa.Create();
ecdsa.ImportFromPem(File.ReadAllText("client-private-key.pem"));

// Sign client assertion
var handler = new JwtSecurityTokenHandler();
var assertion = handler.CreateEncodedJwt(new SecurityTokenDescriptor {
    Issuer = clientId,
    Subject = new ClaimsIdentity(new[] {
        new Claim("sub", clientId),
        new Claim("jti", Guid.NewGuid().ToString()),
    }),
    Audience = tokenEndpoint,
    IssuedAt = DateTime.UtcNow,
    Expires = DateTime.UtcNow.AddMinutes(2),
    SigningCredentials = new SigningCredentials(
        new ECDsaSecurityKey(ecdsa) { KeyId = "2026-04-primary" },
        SecurityAlgorithms.EcdsaSha256
    ),
});

// Request access token
using var http = new HttpClient();
var tokenResp = await http.PostAsync(tokenEndpoint, new FormUrlEncodedContent(new[] {
    KeyValuePair.Create("grant_type", "client_credentials"),
    KeyValuePair.Create("client_id", clientId),
    KeyValuePair.Create("client_assertion_type",
        "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"),
    KeyValuePair.Create("client_assertion", assertion),
    KeyValuePair.Create("scope", "bookings:read"),
}));
tokenResp.EnsureSuccessStatusCode();
var tokenData = await tokenResp.Content.ReadFromJsonAsync<JsonElement>();
var accessToken = tokenData.GetProperty("access_token").GetString()!;

// Call the API
http.DefaultRequestHeaders.Add("Authorization", $"Bearer {accessToken}");
var bookings = await http.GetFromJsonAsync<JsonElement>($"http://api.petanque.life/bookings");
Console.WriteLine($"Found {bookings.GetProperty("total")} bookings");

Node.js

// npm install jose

import { importPKCS8, SignJWT } from 'jose'
import { readFile } from 'fs/promises'

const clientId = 'acme-integration'
const tokenEndpoint = 'http://api.petanque.life/auth/token'

// Load private key
const privateKey = await importPKCS8(
  await readFile('client-private-key.pem', 'utf8'),
  'ES256'
)

// Sign client assertion
const assertion = await new SignJWT({
  sub: clientId,
  jti: crypto.randomUUID(),
})
  .setProtectedHeader({ alg: 'ES256', kid: '2026-04-primary' })
  .setIssuer(clientId)
  .setAudience(tokenEndpoint)
  .setIssuedAt()
  .setExpirationTime('2m')
  .sign(privateKey)

// Request access token
const tokenResp = await fetch(tokenEndpoint, {
  method: 'POST',
  headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
  body: new URLSearchParams({
    grant_type: 'client_credentials',
    client_id: clientId,
    client_assertion_type: 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer',
    client_assertion: assertion,
    scope: 'bookings:read',
  }),
})
const { access_token } = await tokenResp.json()

// Call the API
const bookings = await fetch('http://api.petanque.life/bookings', {
  headers: { Authorization: `Bearer ${access_token}` },
}).then(r => r.json())
console.log(`Found ${bookings.total} bookings`)

cURL

#!/bin/bash
# Complete cURL workflow — useful for debugging, not recommended for production.
# Requires: openssl, jq, curl

CLIENT_ID="acme-integration"
KID="2026-04-primary"
TOKEN_ENDPOINT="http://api.petanque.life/auth/token"
PRIVATE_KEY="client-private-key.pem"

# --- Build JWT header and payload ---
HEADER=$(echo -n '{"alg":"ES256","kid":"'$KID'","typ":"JWT"}' | openssl base64 -e -A | tr '+/' '-_' | tr -d '=')

NOW=$(date +%s)
EXP=$((NOW + 120))
JTI=$(cat /proc/sys/kernel/random/uuid 2>/dev/null || uuidgen)
PAYLOAD=$(echo -n '{"iss":"'$CLIENT_ID'","sub":"'$CLIENT_ID'","aud":"'$TOKEN_ENDPOINT'","jti":"'$JTI'","iat":'$NOW',"exp":'$EXP'}' | openssl base64 -e -A | tr '+/' '-_' | tr -d '=')

# --- Sign ---
SIGNATURE=$(echo -n "$HEADER.$PAYLOAD" | openssl dgst -sha256 -sign "$PRIVATE_KEY" | openssl base64 -e -A | tr '+/' '-_' | tr -d '=')
ASSERTION="$HEADER.$PAYLOAD.$SIGNATURE"

# --- Request access token ---
TOKEN_RESPONSE=$(curl -s -X POST "$TOKEN_ENDPOINT" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials" \
  -d "client_id=$CLIENT_ID" \
  -d "client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer" \
  -d "client_assertion=$ASSERTION" \
  -d "scope=bookings:read")

ACCESS_TOKEN=$(echo "$TOKEN_RESPONSE" | jq -r '.access_token')
echo "Token type: $(echo "$TOKEN_RESPONSE" | jq -r '.token_type')"
echo "Expires in: $(echo "$TOKEN_RESPONSE" | jq -r '.expires_in')s"

# --- Call the API ---
curl -s "http://api.petanque.life/bookings" \
  -H "Authorization: Bearer $ACCESS_TOKEN" | jq .

Concepts

OAuth 2.0 Client Credentials

The API uses the OAuth 2.0 Client Credentials grant (RFC 6749 section 4.4) for machine-to-machine (M2M) authentication. There is no user interaction — your service authenticates directly.

private_key_jwt — Why Asymmetric?

Client authentication uses private_key_jwt (RFC 7523). Instead of a shared secret (client_secret), the client signs a short-lived JWT with its private key. The server verifies the signature using the registered public key. Advantages:

Access Tokens & Bearer

POST /auth/token returns a JWT access token. Default lifetime is 15 minutes (900 seconds). Include it in all requests:

Authorization: Bearer eyJhbGciOiJFUzUxMiIs...

Scopes = Capabilities

Every capability name is also an OAuth 2.0 scope value. They are identical strings. When you request scope=bookings:read in the token request, you are asking for the bookings:read capability. The granted token will contain only the intersection of requested scopes and the client's assigned capabilities.

Rotation & Security

Keys can be rotated without downtime. When you create a new key, the old key gets an active_until date (default 30 days). Both keys work during the overlap period. See the Key Rotation section for step-by-step instructions.

Authentication

3.1 Register Client

Create an API client in the Admin UI under Settings → API Clients. You will receive a client_id and can register a public key. Alternatively, use POST /api-clients to create clients programmatically.

3.2 Generate Keypair

Generate an ES256 (P-256) keypair:

# Python (cryptography library)
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives import serialization

private_key = ec.generate_private_key(ec.SECP256R1())

# Save private key (keep this secret!)
with open("client-private-key.pem", "wb") as f:
    f.write(private_key.private_bytes(
        serialization.Encoding.PEM,
        serialization.PrivateFormat.PKCS8,
        serialization.NoEncryption(),
    ))

# Save public key (register this in Admin)
with open("client-public-key.pem", "wb") as f:
    f.write(private_key.public_key().public_bytes(
        serialization.Encoding.PEM,
        serialization.SubjectPublicKeyInfo,
    ))
// C# — generate and save keypair
using System.Security.Cryptography;

var ecdsa = ECDsa.Create(ECCurve.NamedCurves.nistP256);

// Export private key (keep secret!)
File.WriteAllText("client-private-key.pem", ecdsa.ExportECPrivateKeyPem());

// Export public key (register in Admin)
File.WriteAllText("client-public-key.pem", ecdsa.ExportSubjectPublicKeyInfoPem());
// Node.js — generate keypair
import { generateKeyPair, exportPKCS8, exportSPKI } from 'jose'
import { writeFile } from 'fs/promises'

const { privateKey, publicKey } = await generateKeyPair('ES256')

await writeFile('client-private-key.pem', await exportPKCS8(privateKey))
await writeFile('client-public-key.pem', await exportSPKI(publicKey))
# OpenSSL
openssl ecparam -genkey -name prime256v1 -noout -out client-private-key.pem
openssl ec -in client-private-key.pem -pubout -out client-public-key.pem

3.3 Sign Client Assertion

Create a JWT with these required claims:

ClaimValueDescription
issYour client_idIssuer
subYour client_idSubject
audToken endpoint URLAudience — must match exactly
jtiUnique UUIDPrevents replay — each assertion must be unique
iatCurrent Unix timestampIssued at
expiat + max 5 minExpiry — server rejects assertions older than 5 minutes

Sign with ES256 (preferred), ES384, or ES512. Set the kid header to match your registered key ID.

3.4 Request Access Token

resp = requests.post(token_endpoint, data={
    "grant_type": "client_credentials",
    "client_id": client_id,
    "client_assertion_type": "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
    "client_assertion": assertion,
    "scope": "bookings:read bookings:create",  # space-separated capabilities
})
resp.raise_for_status()
token_data = resp.json()
print(f"Token: {token_data['access_token'][:20]}...")
print(f"Type: {token_data['token_type']}")          # "Bearer"
print(f"Expires in: {token_data['expires_in']}s")    # 900
print(f"Scope: {token_data['scope']}")               # "bookings:read bookings:create"

# Error handling
if resp.status_code != 200:
    error = resp.json()
    print(f"Error: {error['error']} — {error['error_description']}")
var tokenResp = await http.PostAsync(tokenEndpoint, new FormUrlEncodedContent(new[] {
    KeyValuePair.Create("grant_type", "client_credentials"),
    KeyValuePair.Create("client_id", clientId),
    KeyValuePair.Create("client_assertion_type",
        "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"),
    KeyValuePair.Create("client_assertion", assertion),
    KeyValuePair.Create("scope", "bookings:read bookings:create"),
}));

if (!tokenResp.IsSuccessStatusCode)
{
    var error = await tokenResp.Content.ReadFromJsonAsync<JsonElement>();
    Console.WriteLine($"Error: {error.GetProperty("error")} — {error.GetProperty("error_description")}");
    return;
}

var tokenData = await tokenResp.Content.ReadFromJsonAsync<JsonElement>();
var accessToken = tokenData.GetProperty("access_token").GetString()!;
Console.WriteLine($"Expires in: {tokenData.GetProperty("expires_in")}s");
const tokenResp = await fetch(tokenEndpoint, {
  method: 'POST',
  headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
  body: new URLSearchParams({
    grant_type: 'client_credentials',
    client_id: clientId,
    client_assertion_type: 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer',
    client_assertion: assertion,
    scope: 'bookings:read bookings:create',
  }),
})

if (!tokenResp.ok) {
  const error = await tokenResp.json()
  console.error(`Error: ${error.error} — ${error.error_description}`)
  process.exit(1)
}

const { access_token, expires_in, scope } = await tokenResp.json()
console.log(`Expires in: ${expires_in}s, scope: ${scope}`)
curl -X POST 'http://api.petanque.life/auth/token' \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  -d 'grant_type=client_credentials' \
  -d 'client_id=acme-integration' \
  -d 'client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer' \
  -d 'client_assertion=eyJ...' \
  -d 'scope=bookings:read bookings:create'

# Response:
# {
#   "access_token": "eyJ...",
#   "token_type": "Bearer",
#   "expires_in": 900,
#   "scope": "bookings:read bookings:create"
# }

3.5 Call API with Bearer Token

Include the access token in all API requests:

headers = {"Authorization": f"Bearer {access_token}"}

# List bookings
bookings = requests.get(f"{base_url}/bookings", headers=headers).json()

# Create a booking
new_booking = requests.post(f"{base_url}/bookings/", headers=headers, json={
    "product_id": "64a1b2c3d4e5f6789012abcd",
    "start_time": "2026-04-01T08:00:00Z",
    "end_time": "2026-04-01T17:00:00Z",
}).json()
http.DefaultRequestHeaders.Add("Authorization", $"Bearer {accessToken}");

// List bookings
var bookings = await http.GetFromJsonAsync<JsonElement>($"{baseUrl}/bookings");

// Create a booking
var newBooking = await http.PostAsJsonAsync($"{baseUrl}/bookings/", new {
    product_id = "64a1b2c3d4e5f6789012abcd",
    start_time = "2026-04-01T08:00:00Z",
    end_time = "2026-04-01T17:00:00Z",
});
const headers = { Authorization: `Bearer ${access_token}` }

// List bookings
const bookings = await fetch(`${baseUrl}/bookings`, { headers }).then(r => r.json())

// Create a booking
const newBooking = await fetch(`${baseUrl}/bookings/`, {
  method: 'POST',
  headers: { ...headers, 'Content-Type': 'application/json' },
  body: JSON.stringify({
    product_id: '64a1b2c3d4e5f6789012abcd',
    start_time: '2026-04-01T08:00:00Z',
    end_time: '2026-04-01T17:00:00Z',
  }),
}).then(r => r.json())
curl 'http://api.petanque.life/bookings' \
  -H 'Authorization: Bearer eyJ...'

curl -X POST 'http://api.petanque.life/bookings/' \
  -H 'Authorization: Bearer eyJ...' \
  -H 'Content-Type: application/json' \
  -d '{"product_id": "64a1...", "start_time": "2026-04-01T08:00:00Z", "end_time": "2026-04-01T17:00:00Z"}'

3.6 Handle Token Expiry

Access tokens expire after expires_in seconds (default 900 = 15 minutes). There is no refresh endpoint for M2M tokens — request a new one before the current token expires. Recommended pattern:

import time

class TokenManager:
    def __init__(self, client_id, private_key, token_endpoint):
        self.client_id = client_id
        self.private_key = private_key
        self.token_endpoint = token_endpoint
        self._token = None
        self._expires_at = 0

    def get_token(self, scope=""):
        # Refresh 60 seconds before expiry
        if self._token and time.time() < self._expires_at - 60:
            return self._token

        assertion = self._sign_assertion()
        resp = requests.post(self.token_endpoint, data={
            "grant_type": "client_credentials",
            "client_id": self.client_id,
            "client_assertion_type":
                "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
            "client_assertion": assertion,
            "scope": scope,
        })
        resp.raise_for_status()
        data = resp.json()
        self._token = data["access_token"]
        self._expires_at = time.time() + data["expires_in"]
        return self._token

    def _sign_assertion(self):
        now = datetime.now(UTC)
        return jose_jwt.encode(
            header={"alg": "ES256", "kid": "2026-04-primary"},
            payload={
                "iss": self.client_id, "sub": self.client_id,
                "aud": self.token_endpoint,
                "jti": str(uuid.uuid4()),
                "iat": int(now.timestamp()),
                "exp": int((now + timedelta(minutes=2)).timestamp()),
            },
            key=self.private_key,
        ).decode()
public class TokenManager
{
    private readonly string _clientId;
    private readonly ECDsa _key;
    private readonly string _tokenEndpoint;
    private string? _token;
    private DateTimeOffset _expiresAt;

    public TokenManager(string clientId, ECDsa key, string tokenEndpoint)
    {
        _clientId = clientId;
        _key = key;
        _tokenEndpoint = tokenEndpoint;
    }

    public async Task<string> GetTokenAsync(string scope = "")
    {
        if (_token != null && DateTimeOffset.UtcNow < _expiresAt.AddSeconds(-60))
            return _token;

        var assertion = SignAssertion();
        using var http = new HttpClient();
        var resp = await http.PostAsync(_tokenEndpoint, new FormUrlEncodedContent(new[] {
            KeyValuePair.Create("grant_type", "client_credentials"),
            KeyValuePair.Create("client_id", _clientId),
            KeyValuePair.Create("client_assertion_type",
                "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"),
            KeyValuePair.Create("client_assertion", assertion),
            KeyValuePair.Create("scope", scope),
        }));
        resp.EnsureSuccessStatusCode();
        var data = await resp.Content.ReadFromJsonAsync<JsonElement>();
        _token = data.GetProperty("access_token").GetString()!;
        _expiresAt = DateTimeOffset.UtcNow.AddSeconds(data.GetProperty("expires_in").GetInt32());
        return _token;
    }

    private string SignAssertion()
    {
        var handler = new JwtSecurityTokenHandler();
        return handler.CreateEncodedJwt(new SecurityTokenDescriptor {
            Issuer = _clientId,
            Subject = new ClaimsIdentity(new[] {
                new Claim("sub", _clientId),
                new Claim("jti", Guid.NewGuid().ToString()),
            }),
            Audience = _tokenEndpoint,
            IssuedAt = DateTime.UtcNow,
            Expires = DateTime.UtcNow.AddMinutes(2),
            SigningCredentials = new SigningCredentials(
                new ECDsaSecurityKey(_key) { KeyId = "2026-04-primary" },
                SecurityAlgorithms.EcdsaSha256),
        });
    }
}
class TokenManager {
  #token = null
  #expiresAt = 0

  constructor(clientId, privateKey, tokenEndpoint) {
    this.clientId = clientId
    this.privateKey = privateKey
    this.tokenEndpoint = tokenEndpoint
  }

  async getToken(scope = '') {
    if (this.#token && Date.now() / 1000 < this.#expiresAt - 60)
      return this.#token

    const assertion = await new SignJWT({
      sub: this.clientId,
      jti: crypto.randomUUID(),
    })
      .setProtectedHeader({ alg: 'ES256', kid: '2026-04-primary' })
      .setIssuer(this.clientId)
      .setAudience(this.tokenEndpoint)
      .setIssuedAt()
      .setExpirationTime('2m')
      .sign(this.privateKey)

    const resp = await fetch(this.tokenEndpoint, {
      method: 'POST',
      headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
      body: new URLSearchParams({
        grant_type: 'client_credentials',
        client_id: this.clientId,
        client_assertion_type: 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer',
        client_assertion: assertion,
        scope,
      }),
    })
    const data = await resp.json()
    this.#token = data.access_token
    this.#expiresAt = Date.now() / 1000 + data.expires_in
    return this.#token
  }
}
# cURL does not support automatic token refresh.
# Re-run the token request script before expiry.
# Check token expiry:
echo "$TOKEN_RESPONSE" | jq '.expires_in'

3.7 OAuth Scope = Capability

The scope parameter in /auth/token uses capability names as scope values. They are identical strings. For example, scope=bookings:read users:list requests the bookings:read and users:list capabilities. See the Capabilities Reference for the full list.

DPoP (Optional, Advanced)

Demonstrating Proof-of-Possession (RFC 9449) binds access tokens to a client-held keypair, preventing stolen tokens from being used by an attacker.

When to Use

Use DPoP when tokens traverse untrusted networks, when compliance requirements demand proof-of-possession, or when defense-in-depth is important for your integration. Default (Bearer) is sufficient for most use cases.

Flow Overview

1
Generate DPoP Keypair
Separate from the client auth key. Used only for DPoP proofs.
2
Request Token with DPoP
Include a DPoP header with a proof JWT on POST /auth/token. Response has token_type: "DPoP".
3
Use DPoP on Every Request
Authorization: DPoP <token> + DPoP: <proof> header. Each proof is bound to the HTTP method, URL, and the access token hash.

Python Example

from authlib.jose import jwt as jose_jwt, JsonWebKey
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives import serialization
import hashlib, base64, uuid, time

# 1. Generate DPoP keypair (separate from client auth key)
dpop_private = ec.generate_private_key(ec.SECP256R1())
dpop_jwk = JsonWebKey.import_key(dpop_private)
dpop_public_jwk = {
    "kty": dpop_jwk.as_dict()["kty"],
    "crv": dpop_jwk.as_dict()["crv"],
    "x": dpop_jwk.as_dict()["x"],
    "y": dpop_jwk.as_dict()["y"],
}

def create_dpop_proof(method, url, access_token=None):
    payload = {
        "jti": str(uuid.uuid4()),
        "htm": method,
        "htu": url,
        "iat": int(time.time()),
    }
    if access_token:
        # ath = base64url(SHA-256(access_token))
        ath = base64.urlsafe_b64encode(
            hashlib.sha256(access_token.encode()).digest()
        ).rstrip(b"=").decode()
        payload["ath"] = ath

    return jose_jwt.encode(
        header={"alg": "ES256", "typ": "dpop+jwt", "jwk": dpop_public_jwk},
        payload=payload,
        key=dpop_private,
    ).decode()

# 2. Token request with DPoP
dpop_proof = create_dpop_proof("POST", token_endpoint)
resp = requests.post(token_endpoint, data={
    "grant_type": "client_credentials",
    "client_id": client_id,
    "client_assertion_type":
        "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
    "client_assertion": assertion,
    "scope": "bookings:read",
}, headers={"DPoP": dpop_proof})

access_token = resp.json()["access_token"]
# token_type will be "DPoP" instead of "Bearer"

# 3. API call with DPoP
api_url = f"{base_url}/bookings"
api_proof = create_dpop_proof("GET", api_url, access_token)
bookings = requests.get(api_url, headers={
    "Authorization": f"DPoP {access_token}",
    "DPoP": api_proof,
}).json()

Making Requests

Complete examples for setting up an authenticated API client.

Python — Full Client Setup

import requests
from authlib.jose import jwt as jose_jwt
from datetime import datetime, timedelta, UTC
import uuid, time


class ApiClient:
    """M2M API client with automatic token management."""

    def __init__(self, base_url: str, client_id: str, private_key: bytes,
                 kid: str, scope: str = ""):
        self.base_url = base_url
        self.client_id = client_id
        self.private_key = private_key
        self.kid = kid
        self.scope = scope
        self.token_endpoint = f"{base_url}/auth/token"
        self._token = None
        self._expires_at = 0

    def _ensure_token(self):
        if self._token and time.time() < self._expires_at - 60:
            return
        now = datetime.now(UTC)
        assertion = jose_jwt.encode(
            header={"alg": "ES256", "kid": self.kid},
            payload={
                "iss": self.client_id, "sub": self.client_id,
                "aud": self.token_endpoint,
                "jti": str(uuid.uuid4()),
                "iat": int(now.timestamp()),
                "exp": int((now + timedelta(minutes=2)).timestamp()),
            },
            key=self.private_key,
        ).decode()

        resp = requests.post(self.token_endpoint, data={
            "grant_type": "client_credentials",
            "client_id": self.client_id,
            "client_assertion_type":
                "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
            "client_assertion": assertion,
            "scope": self.scope,
        })
        resp.raise_for_status()
        data = resp.json()
        self._token = data["access_token"]
        self._expires_at = time.time() + data["expires_in"]

    def _headers(self):
        self._ensure_token()
        return {
            "Authorization": f"Bearer {self._token}",
            "Content-Type": "application/json",
        }

    def list(self, resource, where=None, page=1, per_page=25, sort="-created_at"):
        params = {"page": page, "per_page": per_page, "sort": sort}
        if where:
            import json
            params["where"] = json.dumps(where)
        resp = requests.get(f"{self.base_url}/{resource}/",
                           params=params, headers=self._headers())
        resp.raise_for_status()
        return resp.json()

    def get(self, resource, item_id):
        resp = requests.get(f"{self.base_url}/{resource}/{item_id}",
                           headers=self._headers())
        resp.raise_for_status()
        return resp.json(), resp.headers.get("ETag", "")

    def create(self, resource, data):
        resp = requests.post(f"{self.base_url}/{resource}/",
                            json=data, headers=self._headers())
        resp.raise_for_status()
        return resp.json(), resp.headers.get("ETag", "")

    def update(self, resource, item_id, data, etag):
        headers = {**self._headers(), "If-Match": etag}
        resp = requests.patch(f"{self.base_url}/{resource}/{item_id}",
                             json=data, headers=headers)
        resp.raise_for_status()
        return resp.json(), resp.headers.get("ETag", "")

    def delete(self, resource, item_id, etag):
        headers = {**self._headers(), "If-Match": etag}
        resp = requests.delete(f"{self.base_url}/{resource}/{item_id}",
                              headers=headers)
        resp.raise_for_status()


# Usage
with open("client-private-key.pem", "rb") as f:
    key = f.read()

api = ApiClient("http://api.petanque.life", "acme-integration", key, "2026-04-primary",
                scope="bookings:read bookings:create")

# List active bookings
bookings = api.list("bookings", where={"status": "active"})
for b in bookings["items"]:
    print(f"{b['_id']} — {b['start_time']}")

# Create a booking
booking, etag = api.create("bookings", {
    "product_id": "64a1b2c3d4e5f6789012abcd",
    "start_time": "2026-04-01T08:00:00Z",
    "end_time": "2026-04-01T17:00:00Z",
})
print(f"Created: {booking['_id']}")

C# — Full Client Setup

using System.Net.Http.Json;
using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Security.Cryptography;

public class ApiClient : IDisposable
{
    private readonly HttpClient _http = new();
    private readonly string _baseUrl;
    private readonly string _clientId;
    private readonly ECDsa _key;
    private readonly string _kid;
    private readonly string _scope;
    private readonly string _tokenEndpoint;
    private string? _token;
    private DateTimeOffset _expiresAt;

    public ApiClient(string baseUrl, string clientId, string keyPath,
                     string kid, string scope = "")
    {
        _baseUrl = baseUrl;
        _clientId = clientId;
        _key = ECDsa.Create();
        _key.ImportFromPem(File.ReadAllText(keyPath));
        _kid = kid;
        _scope = scope;
        _tokenEndpoint = $"{baseUrl}/auth/token";
        _http.Timeout = TimeSpan.FromSeconds(30);
    }

    private async Task EnsureTokenAsync()
    {
        if (_token != null && DateTimeOffset.UtcNow < _expiresAt.AddSeconds(-60))
            return;

        var handler = new JwtSecurityTokenHandler();
        var assertion = handler.CreateEncodedJwt(new SecurityTokenDescriptor {
            Issuer = _clientId,
            Subject = new ClaimsIdentity(new[] {
                new Claim("sub", _clientId),
                new Claim("jti", Guid.NewGuid().ToString()),
            }),
            Audience = _tokenEndpoint,
            IssuedAt = DateTime.UtcNow,
            Expires = DateTime.UtcNow.AddMinutes(2),
            SigningCredentials = new SigningCredentials(
                new ECDsaSecurityKey(_key) { KeyId = _kid },
                SecurityAlgorithms.EcdsaSha256),
        });

        var resp = await _http.PostAsync(_tokenEndpoint, new FormUrlEncodedContent(new[] {
            KeyValuePair.Create("grant_type", "client_credentials"),
            KeyValuePair.Create("client_id", _clientId),
            KeyValuePair.Create("client_assertion_type",
                "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"),
            KeyValuePair.Create("client_assertion", assertion),
            KeyValuePair.Create("scope", _scope),
        }));
        resp.EnsureSuccessStatusCode();
        var data = await resp.Content.ReadFromJsonAsync<JsonElement>();
        _token = data.GetProperty("access_token").GetString()!;
        _expiresAt = DateTimeOffset.UtcNow.AddSeconds(data.GetProperty("expires_in").GetInt32());
        _http.DefaultRequestHeaders.Remove("Authorization");
        _http.DefaultRequestHeaders.Add("Authorization", $"Bearer {_token}");
    }

    public async Task<JsonElement> ListAsync(string resource, string? where = null,
        int page = 1, int perPage = 25, string sort = "-created_at")
    {
        await EnsureTokenAsync();
        var query = $"?page={page}&per_page={perPage}&sort={sort}";
        if (where != null)
            query += $"&where={Uri.EscapeDataString(where)}";
        var resp = await _http.GetAsync($"{_baseUrl}/{resource}/{query}");
        resp.EnsureSuccessStatusCode();
        return await resp.Content.ReadFromJsonAsync<JsonElement>();
    }

    public async Task<(JsonElement Item, string ETag)> GetAsync(string resource, string id)
    {
        await EnsureTokenAsync();
        var resp = await _http.GetAsync($"{_baseUrl}/{resource}/{id}");
        resp.EnsureSuccessStatusCode();
        var item = await resp.Content.ReadFromJsonAsync<JsonElement>();
        return (item, resp.Headers.ETag?.Tag ?? "");
    }

    public async Task<(JsonElement Item, string ETag)> CreateAsync(string resource, object data)
    {
        await EnsureTokenAsync();
        var resp = await _http.PostAsJsonAsync($"{_baseUrl}/{resource}/", data);
        resp.EnsureSuccessStatusCode();
        var item = await resp.Content.ReadFromJsonAsync<JsonElement>();
        return (item, resp.Headers.ETag?.Tag ?? "");
    }

    public async Task<(JsonElement Item, string ETag)> UpdateAsync(
        string resource, string id, object data, string etag)
    {
        await EnsureTokenAsync();
        var request = new HttpRequestMessage(HttpMethod.Patch,
            $"{_baseUrl}/{resource}/{id}")
        { Content = JsonContent.Create(data) };
        request.Headers.Add("If-Match", etag);
        var resp = await _http.SendAsync(request);
        resp.EnsureSuccessStatusCode();
        var item = await resp.Content.ReadFromJsonAsync<JsonElement>();
        return (item, resp.Headers.ETag?.Tag ?? "");
    }

    public async Task DeleteAsync(string resource, string id, string etag)
    {
        await EnsureTokenAsync();
        var request = new HttpRequestMessage(HttpMethod.Delete,
            $"{_baseUrl}/{resource}/{id}");
        request.Headers.Add("If-Match", etag);
        var resp = await _http.SendAsync(request);
        resp.EnsureSuccessStatusCode();
    }

    public void Dispose() => _http.Dispose();
}

Filtering & Querying

All list endpoints support two filtering styles that can be combined.

1. JSON Where Clause

Pass a where query parameter with a JSON object.

# Simple equality
products = api.list("products", where={"status": "active"})

# Multiple conditions (AND)
products = api.list("products", where={
    "status": "active",
    "city": "New York"
})

# Comparison operators
charges = api.list("service-charges", where={"amount": {"$gt": 500}})

# Date ranges
orders = api.list("orders", where={
    "start_time": {"$gte": "2026-04-01T00:00:00Z"},
    "end_time": {"$lte": "2026-04-30T23:59:59Z"}
})

# OR queries
orders = api.list("orders", where={
    "$or": [{"status": "active"}, {"status": "confirmed"}]
})

# Regex (case-insensitive)
products = api.list("products", where={
    "name": {"$regex": "^Prem", "$options": "i"}
})
// Simple equality
var products = await api.ListAsync("products",
    where: "{\"status\":\"active\"}");

// Comparison operators
var charges = await api.ListAsync("service-charges",
    where: "{\"amount\":{\"$gt\":500}}");

// OR queries
var active = await api.ListAsync("orders",
    where: "{\"$or\":[{\"status\":\"active\"},{\"status\":\"confirmed\"}]}");
// JSON where
const products = await fetch(
  `${baseUrl}/products/?where=${encodeURIComponent(JSON.stringify({ status: 'active' }))}`,
  { headers }
).then(r => r.json())

// Comparison operators
const charges = await fetch(
  `${baseUrl}/service-charges/?where=${encodeURIComponent(JSON.stringify({ amount: { $gt: 500 } }))}`,
  { headers }
).then(r => r.json())
# Simple equality
curl -G 'http://api.petanque.life/products/' --data-urlencode 'where={"status":"active"}'

# Comparison operators
curl -G 'http://api.petanque.life/service-charges/' --data-urlencode 'where={"amount":{"$gt":500}}'

# OR queries
curl -G 'http://api.petanque.life/orders/' \
  --data-urlencode 'where={"$or":[{"status":"active"},{"status":"confirmed"}]}'

Allowed Operators

$gt, $gteGreater than / greater than or equal
$lt, $lteLess than / less than or equal
$neNot equal
$in, $ninIn / not in array
$existsField exists (true/false)
$regexRegular expression match
$and, $or, $notLogical operators
Dangerous operators like $where, $expr, and $function are blocked to prevent injection attacks.

2. Query Parameter Filters

# Equality
GET /products/?status=active&city=New York

# Operators via __suffix
GET /service-charges/?amount__gt=500
GET /orders/?status__in=active,confirmed
GET /products/?name__regex=^Prem
GET /subscriptions/?valid_until__exists=false

Sorting

# Newest first (default)
orders = api.list("orders", sort="-created_at")

# Sort by name ascending
products = api.list("products", sort="name")

# Multiple sort fields
orders = api.list("orders", sort="-start_time,status")
// Newest first
var orders = await api.ListAsync("orders", sort: "-created_at");

// Sort by name ascending
var products = await api.ListAsync("products", sort: "name");
// Newest first
const orders = await fetch(`${baseUrl}/orders/?sort=-created_at`, { headers }).then(r => r.json())
curl 'http://api.petanque.life/orders/?sort=-start_time,status' -H 'Authorization: Bearer eyJ...'

Field Selection

# Only return specific fields (reduces payload size)
GET /products/?fields=name,status,city,total_items

Pagination

All list endpoints return paginated results. Default: 25 items, max 100.

# Page through all results
page = 1
all_items = []
while True:
    result = api.list("orders", where={"status": "active"},
                      page=page, per_page=100)
    all_items.extend(result["items"])
    if page * 100 >= result["total"]:
        break
    page += 1

print(f"Fetched all {len(all_items)} orders")
// Page through all results
var allItems = new List<JsonElement>();
var page = 1;
while (true)
{
    var result = await api.ListAsync("orders",
        where: "{\"status\":\"active\"}", page: page, perPage: 100);
    // ... add to list
    if (page * 100 >= result.GetProperty("total").GetInt32()) break;
    page++;
}
let page = 1
const allItems = []
while (true) {
  const result = await fetch(
    `${baseUrl}/orders/?where=${encodeURIComponent('{"status":"active"}')}` +
    `&page=${page}&per_page=100`,
    { headers }
  ).then(r => r.json())
  allItems.push(...result.items)
  if (page * 100 >= result.total) break
  page++
}
curl 'http://api.petanque.life/orders/?page=2&per_page=50' -H 'Authorization: Bearer eyJ...'

Response Format

{
  "items": [...],
  "total": 142,
  "page": 2,
  "per_page": 50
}

Response Headers

X-Total-CountTotal matching items
X-PageCurrent page
X-Per-PageItems per page
X-Total-PagesTotal pages

Concurrency Control (ETag)

All update and delete operations require optimistic concurrency control. This prevents lost updates when multiple clients modify the same document.

1
GET the resource — response includes ETag header
2
PATCH/PUT/DELETE — include If-Match: "the-etag"
3
If modified by someone else → 412 Precondition Failed — retry from step 1
# Get-then-update pattern
product, etag = api.get("products", "64a1...")

# Update with ETag
updated, new_etag = api.update("products", "64a1...", {
    "status": "maintenance"
}, etag)

# Handle conflicts
try:
    api.update("products", "64a1...", {"status": "active"}, etag)
except requests.HTTPError as e:
    if e.response.status_code == 412:
        print("Conflict! Re-fetch and retry.")
        product, etag = api.get("products", "64a1...")
    elif e.response.status_code == 428:
        print("Missing If-Match header!")
// Get-then-update pattern
var (product, etag) = await api.GetAsync("products", "64a1...");

// Update with ETag
var (updated, newEtag) = await api.UpdateAsync("products", "64a1...",
    new { status = "maintenance" }, etag);

// Handle conflicts
try
{
    await api.UpdateAsync("products", "64a1...",
        new { status = "active" }, etag);
}
catch (HttpRequestException ex) when (ex.StatusCode == System.Net.HttpStatusCode.PreconditionFailed)
{
    Console.WriteLine("Conflict! Re-fetch and retry.");
    (product, etag) = await api.GetAsync("products", "64a1...");
}
// Get-then-update
const getResp = await fetch(`${baseUrl}/products/64a1...`, { headers })
const product = await getResp.json()
const etag = getResp.headers.get('etag')

// Update with ETag
const updateResp = await fetch(`${baseUrl}/products/64a1...`, {
  method: 'PATCH',
  headers: { ...headers, 'Content-Type': 'application/json', 'If-Match': etag },
  body: JSON.stringify({ status: 'maintenance' }),
})

if (updateResp.status === 412) {
  console.log('Conflict! Re-fetch and retry.')
}
# 1. Get the resource and its ETag
curl -i 'http://api.petanque.life/products/64a1...' -H 'Authorization: Bearer eyJ...'
# Response header: ETag: "abc123"

# 2. Update with If-Match
curl -X PATCH 'http://api.petanque.life/products/64a1...' \
  -H 'Authorization: Bearer eyJ...' \
  -H 'If-Match: "abc123"' \
  -H 'Content-Type: application/json' \
  -d '{"status": "maintenance"}'

CRUD Lifecycle

Complete example: managing resources from creation to deletion.

# === CREATE ===
product, etag = api.create("products", {
    "name": "Premium Suite",
    "address": "5th Avenue 12",
    "city": "New York",
    "zip_code": "10001",
    "total_items": 45,
})
product_id = product["_id"]
print(f"Created: {product['name']} ({product_id})")

# === READ ===
product, etag = api.get("products", product_id)
print(f"Status: {product['status']}")

# === UPDATE (partial) ===
product, etag = api.update("products", product_id, {
    "is_published": True,
}, etag)

# === LIST with filters ===
published = api.list("products", where={
    "is_published": True,
    "city": "New York",
})
print(f"{published['total']} published products in New York")

# === DELETE (will fail if children exist — cascade deny) ===
try:
    api.delete("products", product_id, etag)
except requests.HTTPError as e:
    if e.response.status_code == 409:
        print("Cannot delete — has related items (cascade deny)")
// === CREATE ===
var (product, etag) = await api.CreateAsync("products", new {
    name = "Premium Suite",
    address = "5th Avenue 12",
    city = "New York",
    zip_code = "10001",
    total_items = 45,
});
var productId = product.GetProperty("_id").GetString()!;

// === READ ===
(product, etag) = await api.GetAsync("products", productId);

// === UPDATE ===
(product, etag) = await api.UpdateAsync("products", productId, new {
    is_published = true,
}, etag);

// === DELETE ===
await api.DeleteAsync("products", productId, etag);
// CREATE
const { _id: productId } = await fetch(`${baseUrl}/products/`, {
  method: 'POST',
  headers: { ...headers, 'Content-Type': 'application/json' },
  body: JSON.stringify({ name: 'Premium Suite', city: 'New York' }),
}).then(r => r.json())

// DELETE
await fetch(`${baseUrl}/products/${productId}`, {
  method: 'DELETE',
  headers: { ...headers, 'If-Match': etag },
})
# Create
curl -X POST 'http://api.petanque.life/products/' \
  -H 'Authorization: Bearer eyJ...' \
  -H 'Content-Type: application/json' \
  -d '{"name": "Premium Suite", "city": "New York"}'

# Delete (requires ETag from GET)
curl -X DELETE 'http://api.petanque.life/products/64a1...' \
  -H 'Authorization: Bearer eyJ...' \
  -H 'If-Match: "abc123"'

Cascade Operations

The API maintains data consistency automatically.

Cascade Update

When a referenced resource changes, related fields are updated automatically. For example, renaming a product updates product_name on all orders, inventory items, subscriptions, and service charges.

Fields that are cascade-updated are marked as readonly. You don't set them — the server handles it.

Cascade Delete Rules

RuleBehaviorExample
deny Block deletion if children exist (409 Conflict) Cannot delete product that has inventory items
null Set reference to null on children Deleting a manager nullifies manager_id
delete Delete all children Not currently used

Capabilities Reference

Every row in this table is both a capability name (for internal enforcement via requires()) and an OAuth 2.0 scope value (for the scope parameter in /auth/token requests). They are always identical strings.
Capability / ScopeActionResourceDescription
federation:managecustomManage federation settings and configuration
federation:viewcustomView federation information
federation:configurecustomConfigure federation rules, fees, and policies
federation:admincustomFull federation administration access
continuity:readcustomRead continuity export status, manifest and run history
continuity:configurecustomUpdate continuity export public encryption key + trigger test-destination dry-runs
district:managecustomManage district settings and operations
district:viewcustomView district information
district:createcustomCreate new districts
district:deletecustomDelete districts
club:managecustomManage club settings and operations
club:viewcustomView club information
club:memberscustomManage club member roster
club:createcustomCreate new clubs
club:deletecustomDelete clubs
license:issuecustomIssue new licenses to players
license:verifycustomVerify license status
license:suspendcustomSuspend active licenses
license:revokecustomRevoke licenses permanently
license:viewcustomView license details
license:renewcustomRenew existing licenses
league:managecustomManage league operations (status transitions, eligibility checks)
competition:createcustomCreate new competitions
competition:sanctioncustomSanction and approve competitions
competition:managecustomManage competition operations (draw, schedule)
competition:viewcustomView competition information and results
competition:deletecustomDelete competitions
competition:registercustomRegister for competitions (entry, payment, validation)
competition:awardcustomManage competition awards (POTM, tournament awards)
competition:publish_resultscustomPublish finalized competition results publicly
competition:manage_seriescustomCreate, edit, and manage championship series
calendar:createcustomCreate calendar events
calendar:managecustomManage calendar events (edit, cancel)
calendar:approvecustomApprove or reject calendar events
calendar:viewcustomView calendar events
match:officiatecustomOfficiate matches as umpire
match:scorecustomEnter and edit match scores
match:viewcustomView match details and results
match:managecustomFull match management (assign courts, override)
match:scorekeepercustomAssign and manage official scorekeepers for matches
match:signcustomSign completed match results as official or delegate
match:live_scorecustomStart and manage live scoring sessions
ranking:managecustomManage ranking configurations and point rules
ranking:viewcustomView rankings and standings
ranking:calculatecustomTrigger ranking recalculations
ranking:disputecustomSubmit and manage ranking point disputes
ranking:resolve_disputecustomResolve ranking disputes (accept/reject)
ranking:qualification_snapshotcustomCreate and manage qualification deadline snapshots
ranking:cross_tenant_elocustomManage cross-tenant ELO configuration
elo:viewcustomView ELO ratings and leaderboards
elo:managecustomManage ELO settings and K-factor configuration
elo:calculatecustomTrigger ELO rating recalculations after matches
finance:managecustomManage financial operations and budgets
finance:viewcustomView financial reports and transactions
finance:approvecustomApprove financial transactions and budgets
finance:reconcilecustomImport bank statements and perform bank reconciliation
discipline:managecustomManage disciplinary cases and sanctions
discipline:viewcustomView disciplinary records
discipline:createcustomCreate new disciplinary cases
discipline:case:readcustomRead discipline case-spine cases (within actor's org scope)
discipline:case:writecustomCreate/edit discipline cases (intake, transitions, escalation)
discipline:case:decidecustomConfirm panel decisions and apply sanctions
discipline:case:appealcustomFile appeals on behalf of accused parties
discipline:case:publishcustomPublish or correct anonymised public protocols
discipline:case:lift_sanctioncustomLift or modify active sanctions outside the appeal process
discipline:evidence:writecustomUpload, supersede, withdraw, and redact evidence
discipline:evidence:verifycustomRun evidence-chain integrity verification
discipline:catalog:writecustomManage tenant sanction catalog and offence vocabulary
discipline:precedent:readcustomSearch precedent records and recommended ranges
discipline:cross_tenant:publishcustomPublish suspensions to the FIPJP cross-tenant registry
discipline:cross_tenant:readcustomRead the FIPJP cross-tenant suspension registry
transfer:approvecustomApprove player transfers
transfer:requestcustomRequest player transfers
transfer:viewcustomView transfer status and history
player:managecustomManage player profiles and registrations
player:viewcustomView player profiles
player:registercustomRegister new players
official:managecustomManage official registrations and assignments
official:certifycustomCertify and grade officials (umpire levels)
official:viewcustomView official profiles and certifications
coach:managecustomManage coach registrations and assignments
coach:certifycustomCertify and grade coaches
coach:viewcustomView coach profiles and certifications
officials.training.readcustomView umpire training modules and exam content
officials.training.managecustomCreate/edit umpire training modules and exams
officials.exams.takecustomParticipate in practice exams and certification attempts
officials.exams.reviewcustomManually review/grade scenario exam questions
officials.commission.managecustomManage evaluation templates and read all evaluations
mentor.servecustomAccept mentor assignments and serve as mentor
mentor.thread.accesscustomAccess mentor threads (computed dynamically per assignment)
finance.fx.managecustomManage FX rates and convert currencies
finance.fx.readcustomRead FX rates and conversions
coaching.content.drill.readcustomView drill templates
coaching.content.drill.writecustomCreate or edit drill templates
coaching.content.drill.publishcustomMark a drill template as shareable cross-tenant
coaching.content.session.readcustomView session plans
coaching.content.session.writecustomCreate or edit session plans + export PDF
coaching.content.tactical.readcustomView tactical scenarios
coaching.content.tactical.writecustomCreate or edit tactical scenarios
coaching.content.mental.readcustomView mental skills tracks
coaching.content.mental.writecustomCreate or edit mental skills tracks
coaching.content.track.readcustomView learning tracks
coaching.content.track.writecustomCreate or edit learning tracks
coaching.school.readcustomView PE curriculum packs and school activations
coaching.school.writecustomCreate PE curriculum packs and activate them on partnerships
coaching.umpire.readcustomView umpire exam tracks, item bank and practice exams
coaching.umpire.writecustomCreate or edit umpire exam tracks, item bank and practice exams
coaching.mentor.readcustomView mentor pairings and session logs
coaching.mentor.writecustomCreate or edit mentor pairings and session logs
coaching.mentor.matchcustomRun mentor-matching suggestions
cms:editcustomEdit CMS content and pages
cms:publishcustomPublish CMS content
cms:viewcustomView unpublished CMS drafts
cms:edit:federationcustomEdit federation-level CMS site content
cms:edit:clubcustomEdit club-level CMS site content only
cms:edit:regioncustomEdit region/district-level CMS site content
cms:approvecustomApprove CMS content for publication (review → published)
cms:publish:immediatecustomPublish CMS content immediately
cms:publish:schedulecustomSchedule CMS content for future publication
cms:widget:managecustomManage CMS dynamic data widget configurations
delegate:managecustomManage delegate appointments, reports, and templates
delegate:viewcustomView delegate assignments, observations, and reports
safeguarding:managecustomManage safeguarding policies and officers
safeguarding:reportcustomSubmit safeguarding incident reports
safeguarding:viewcustomView safeguarding records and reports
safeguarding:trainingcustomManage safeguarding training requirements and records
medical_emergency:viewcustomView emergency contacts and medical info at events
medical_emergency:managecustomManage medical emergency incidents and compliance checks
medical_emergency:reportcustomReport medical emergency incidents at events
integrity:viewcustomView integrity alerts, reports and investigations
integrity:managecustomManage integrity investigations and equipment checks
integrity:reportcustomSubmit suspicious activity reports
dispute:viewcustomView disputes and resolution history
dispute:managecustomManage disputes, escalation and mediation
audit:viewcustomView audit logs and system activity
election:managecustomManage elections (create, configure, cancel)
election:viewcustomView elections and results
election:nominatecustomRegister as candidate or nominate others
election:votecustomCast votes in elections
election:countcustomCount votes and publish results
election:proxycustomManage proxy voting authorizations
selection:managecustomManage national team selection and squads
selection:viewcustomView national team selections
membership:managecustomManage club memberships and registrations
membership:viewcustomView membership records
equipment:managecustomManage personal equipment inventory
equipment:viewcustomView equipment information
equipment:createcustomRegister new equipment items
equipment:deletecustomDelete equipment items
casual_game:createcustomCreate casual games
casual_game:playcustomPlay and score casual games
casual_game:viewcustomView casual game history and results
casual_game:invitecustomInvite players to casual games
casual_game:availabilitycustomSet looking-for-match availability
casual_game:club_nightcustomCreate and manage club night events
record:viewcustomView records and record history
record:managecustomManage and verify records
milestone:viewcustomView milestones and milestone definitions
milestone:managecustomManage milestone definitions and detect milestones
partnership:viewcustomView partnerships and partnership statistics
partnership:managecustomCreate, update, dissolve, and record results for partnerships
gamification:managecustomManage gamification settings, badges, and challenges
gamification:viewcustomView badges, achievements, XP, and leaderboards
gamification:awardcustomAward badges and XP to players
gamification:predictcustomMake predictions on matches and tournaments
gamification:fantasycustomCreate and manage fantasy teams
gamification:challengecustomJoin and participate in challenges
community:reactcustomAdd reactions/kudos to activity feed entries
community:sharecustomShare achievements to social media
community:year_in_reviewcustomGenerate and view year-in-review summaries
gdpr:exportcustomExport personal data (GDPR Art. 20)
gdpr:deletecustomRequest data deletion (GDPR Art. 17)
gdpr:viewcustomView GDPR requests and deletion status
gdpr:managecustomManage GDPR requests and data retention policies
consent:managecustomManage consent records and parental consent
consent:viewcustomView consent records
gdpr:dpo:readcustomRead DPO console — consent scopes, access requests, cross-tenant overview
gdpr:dpo:writecustomPublish consent-scope versions and file/transition DPO access requests
marketplace:managecustomManage marketplace listings, retailers, and boule bars
marketplace:viewcustomView marketplace listings and retailer catalog
marketplace:createcustomCreate marketplace listings
marketplace:moderatecustomModerate listings, verify retailers and boule bars
sponsor:managecustomManage sponsors, contracts, and ad placements
sponsor:viewcustomView sponsor information and exposure reports
sponsor:analytics:readcustomRead pre-aggregated sponsor visibility analytics rollups
rule:managecustomManage rule documents, versions, and interpretations
rule:viewcustomView rule documents and interpretations
rule:proposecustomPropose rule changes for review
rule:approvecustomApprove or reject rule change proposals
rule:translatecustomManage rule translations
webhook:managecustomManage webhook subscriptions and view delivery logs
webhook:viewcustomView webhook subscriptions and delivery status
api_token:managecustomCreate, revoke, and manage M2M API tokens
api_token:viewcustomView M2M API token metadata
statistics:viewcustomView statistics and analytics dashboards
statistics:managecustomManage dashboards, scheduled reports, and export configs
statistics:exportcustomExport reports (PDF, CSV, Excel)
statistics:federation_reportscustomView and generate federation-level reports
statistics:global_reportscustomView and generate FIPJP global analytics
profile:read_dobcustomRead exact date of birth on user profiles
profile:read_license_numbercustomRead player license numbers
profile:read_contactcustomRead contact details on administrator profiles
profile:read_sensitivecustomRead all sensitive profile fields
data_visibility:managecustomManage public field configuration and data visibility rules
data_visibility:viewcustomView public field configuration
access:debugcustomInspect effective capabilities and role resolution traces
role_request:createcustomRequest a role assignment
role_request:viewcustomView role requests
role_request:approvecustomApprove or deny role requests
scoreboard:managecustomManage scoreboard devices (create, update, retire)
scoreboard:viewcustomView scoreboard details and telemetry
scoreboard:custodycustomTransfer scoreboard custody between org nodes
chain.analytics.readcustomView chain-venue analytics dashboards and drill-downs
chain.analytics.exportcustomExport chain-venue analytics reports (CSV/PDF/XLSX)
analytics.scout.readcustomView scout reports (team or federation visibility)
analytics.scout.writecustomCreate, edit and publish scout reports
analytics.federation.readcustomRead federation→club engagement metrics and conversion attribution
analytics.grants.readcustomRead grant workflows and impact reports
analytics.grants.writecustomCreate grants, run state transitions and build impact reports
analytics.cohort.readcustomRun and view cohort-comparison statistical queries
para.classification.readcustomRead para-pétanque classifications and panel rosters
para.classification.writecustomCreate provisional classifications and update consent flags
para.classification.decidecustomRecord panel decisions and revoke para-pétanque classifications
events.marshal.readcustomView court marshal live ops board and acknowledge threshold alerts (event-scoped)
federation.engagement.readcustomRead federation→club engagement metrics, silent-club flagging and per-club drill-downs
government.grants.readcustomRead ministry/government grant applications and impact reports
government.grants.writecustomCreate, edit and progress ministry/government grant applications and reports
self_registration.read_configcustomRead self-registration configuration (admin)
partners:listlistpartnersList partners
partners:readreadpartnersRead a single partners item
partners:createcreatepartnersCreate a new partners item
partners:updateupdatepartnersUpdate a partners item
partners:deletedeletepartnersDelete a partners item
tenants:listlisttenantsList tenants
tenants:readreadtenantsRead a single tenants item
tenants:createcreatetenantsCreate a new tenants item
tenants:updateupdatetenantsUpdate a tenants item
tenants:deletedeletetenantsDelete a tenants item
users:listlistusersList users
users:readreadusersRead a single users item
users:createcreateusersCreate a new users item
users:updateupdateusersUpdate a users item
users:deletedeleteusersDelete a users item
roles:listlistrolesList roles
roles:readreadrolesRead a single roles item
roles:createcreaterolesCreate a new roles item
roles:updateupdaterolesUpdate a roles item
roles:deletedeleterolesDelete a roles item
role-assignments:listlistrole-assignmentsList role-assignments
role-assignments:readreadrole-assignmentsRead a single role-assignments item
role-assignments:createcreaterole-assignmentsCreate a new role-assignments item
role-assignments:updateupdaterole-assignmentsUpdate a role-assignments item
role-assignments:deletedeleterole-assignmentsDelete a role-assignments item
imports/templates:listlistimports/templatesList imports/templates
imports/templates:readreadimports/templatesRead a single imports/templates item
imports/templates:createcreateimports/templatesCreate a new imports/templates item
imports/templates:updateupdateimports/templatesUpdate a imports/templates item
imports/templates:deletedeleteimports/templatesDelete a imports/templates item
imports/sources:listlistimports/sourcesList imports/sources
imports/sources:readreadimports/sourcesRead a single imports/sources item
imports/sources:createcreateimports/sourcesCreate a new imports/sources item
imports/sources:updateupdateimports/sourcesUpdate a imports/sources item
imports/sources:deletedeleteimports/sourcesDelete a imports/sources item
rejected-records:listlistrejected-recordsList rejected-records
rejected-records:readreadrejected-recordsRead a single rejected-records item
rejected-records:createcreaterejected-recordsCreate a new rejected-records item
rejected-records:updateupdaterejected-recordsUpdate a rejected-records item
rejected-records:deletedeleterejected-recordsDelete a rejected-records item
alert-channels:listlistalert-channelsList alert-channels
alert-channels:readreadalert-channelsRead a single alert-channels item
alert-channels:createcreatealert-channelsCreate a new alert-channels item
alert-channels:updateupdatealert-channelsUpdate a alert-channels item
alert-channels:deletedeletealert-channelsDelete a alert-channels item
alert-routing-rules:listlistalert-routing-rulesList alert-routing-rules
alert-routing-rules:readreadalert-routing-rulesRead a single alert-routing-rules item
alert-routing-rules:createcreatealert-routing-rulesCreate a new alert-routing-rules item
alert-routing-rules:updateupdatealert-routing-rulesUpdate a alert-routing-rules item
alert-routing-rules:deletedeletealert-routing-rulesDelete a alert-routing-rules item
tenant-configs:listlisttenant-configsList tenant-configs
tenant-configs:readreadtenant-configsRead a single tenant-configs item
tenant-configs:createcreatetenant-configsCreate a new tenant-configs item
tenant-configs:updateupdatetenant-configsUpdate a tenant-configs item
tenant-configs:deletedeletetenant-configsDelete a tenant-configs item
seasons:listlistseasonsList seasons
seasons:readreadseasonsRead a single seasons item
seasons:createcreateseasonsCreate a new seasons item
seasons:updateupdateseasonsUpdate a seasons item
seasons:deletedeleteseasonsDelete a seasons item
i18n/bundles:listlisti18n/bundlesList i18n/bundles
i18n/bundles:readreadi18n/bundlesRead a single i18n/bundles item
i18n/bundles:createcreatei18n/bundlesCreate a new i18n/bundles item
i18n/bundles:updateupdatei18n/bundlesUpdate a i18n/bundles item
i18n/bundles:deletedeletei18n/bundlesDelete a i18n/bundles item
self.profile.accesscustomSelf-service: read and manage the caller's own profile
self.auth.managecustomSelf-service: manage the caller's own auth, 2FA, WebAuthn, sessions
self.feedback.submitcustomSelf-service: submit feedback from the signed-in user
sys.console.accesscustomAccess the cross-tenant system console
admin.tenant.accesscustomAccess the tenant administration surface
admin.agreements.managecustomManage tenant agreements and feature contracts
admin.partners.managecustomManage platform partners and integrations
admin.tenants.managecustomManage tenants (create, archive, configure sub-tenants)
admin.roles.managecustomManage roles, capabilities, and role assignments
admin.developer_api.managecustomManage developer API tokens, M2M clients and quotas
admin.notifications.managecustomManage tenant notification templates and scheduling
admin.templates.managecustomManage tenant template libraries (clone, preview, version, rollback)
admin.audit.viewcustomRead and export tenant audit logs
admin.imports.managecustomManage file imports, migrations, and rejected-record review
admin.jobs.managecustomManage background jobs and schedules
admin.claims.managecustomManage insurance claims, escalations and payment plans
admin.collections.managecustomManage debt collection workflows and assignments
admin.cost_types.managecustomManage chart-of-accounts cost types
admin.holiday_calendars.managecustomManage tenant holiday calendars
admin.webhooks.managecustomManage webhook subscriptions, replays and dead-letter queue
privacy.gdpr.managecustomAct on behalf of data subjects (exports, deletions, consents)
tenant-config.managecustomEdit tenant configuration (branding, currencies, VAT, policies)
finance.reconciliation.viewcustomRead cross-tenant client-fund reconciliation reports
finance.settlements.managecustomManage cross-tenant settlement batches and payouts
finance.revenue.managecustomManage revenue-split rules and calculations
finance.accounts.managecustomManage payment/account configuration across tenants
domain.users.accesscustomCatch-all access to 'users' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.photo_galleries.accesscustomCatch-all access to 'photo-galleries' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.photographer_accreditations.accesscustomCatch-all access to 'photographer-accreditations' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.photo_consents.accesscustomCatch-all access to 'photo-consents' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.video_highlights.accesscustomCatch-all access to 'video-highlights' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.media_items.accesscustomCatch-all access to 'media-items' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.press_kits.accesscustomCatch-all access to 'press-kits' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.calendar_events.accesscustomCatch-all access to 'calendar-events' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.calendar_subscriptions.accesscustomCatch-all access to 'calendar-subscriptions' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.calendar_notification_preferences.accesscustomCatch-all access to 'calendar-notification-preferences' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.calendar_reminders.accesscustomCatch-all access to 'calendar-reminders' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.club.accesscustomCatch-all access to 'club' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.affiliate.accesscustomCatch-all access to 'affiliate' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.seasons.accesscustomCatch-all access to 'seasons' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.license_types.accesscustomCatch-all access to 'license-types' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.license_applications.accesscustomCatch-all access to 'license-applications' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.license_fee_configs.accesscustomCatch-all access to 'license-fee-configs' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.license_fee_overrides.accesscustomCatch-all access to 'license-fee-overrides' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.license_fees.accesscustomCatch-all access to 'license-fees' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.licenses.accesscustomCatch-all access to 'licenses' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.license_audit.accesscustomCatch-all access to 'license-audit' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.medical_certificates.accesscustomCatch-all access to 'medical-certificates' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.insurance.accesscustomCatch-all access to 'insurance' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.international_licenses.accesscustomCatch-all access to 'international-licenses' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.foreign_licenses.accesscustomCatch-all access to 'foreign-licenses' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.unauthorized_player_violations.accesscustomCatch-all access to 'unauthorized-player-violations' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.districts.accesscustomCatch-all access to 'districts' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.clubs.accesscustomCatch-all access to 'clubs' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.players.accesscustomCatch-all access to 'players' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.org_nodes.accesscustomCatch-all access to 'org-nodes' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.federations.accesscustomCatch-all access to 'federations' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.federation_onboarding.accesscustomCatch-all access to 'federation-onboarding' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.player_profiles.accesscustomCatch-all access to 'player-profiles' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.official_profiles.accesscustomCatch-all access to 'official-profiles' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.coach_profiles.accesscustomCatch-all access to 'coach-profiles' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.administrator_profiles.accesscustomCatch-all access to 'administrator-profiles' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.hierarchy_access.accesscustomCatch-all access to 'hierarchy-access' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.equipment.accesscustomCatch-all access to 'equipment' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.equipment_wishlist.accesscustomCatch-all access to 'equipment-wishlist' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.pro_equipment_profiles.accesscustomCatch-all access to 'pro-equipment-profiles' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.equipment_catalog.accesscustomCatch-all access to 'equipment-catalog' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.homologations.accesscustomCatch-all access to 'homologations' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.product_reviews.accesscustomCatch-all access to 'product-reviews' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.accessory_catalog.accesscustomCatch-all access to 'accessory-catalog' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.apparel_catalog.accesscustomCatch-all access to 'apparel-catalog' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.product_announcements.accesscustomCatch-all access to 'product-announcements' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.cms.accesscustomCatch-all access to 'cms' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.tenant.accesscustomCatch-all access to 'tenant' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.casual_games.accesscustomCatch-all access to 'casual-games' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.game_invites.accesscustomCatch-all access to 'game-invites' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.player_availability.accesscustomCatch-all access to 'player-availability' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.club_nights.accesscustomCatch-all access to 'club-nights' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.badge_definitions.accesscustomCatch-all access to 'badge-definitions' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.player_badges.accesscustomCatch-all access to 'player-badges' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.player_xp.accesscustomCatch-all access to 'player-xp' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.challenges.accesscustomCatch-all access to 'challenges' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.player_challenges.accesscustomCatch-all access to 'player-challenges' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.predictions.accesscustomCatch-all access to 'predictions' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.fantasy_leagues.accesscustomCatch-all access to 'fantasy-leagues' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.fantasy_teams.accesscustomCatch-all access to 'fantasy-teams' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.leaderboards.accesscustomCatch-all access to 'leaderboards' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.milestones.accesscustomCatch-all access to 'milestones' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.match_challenges.accesscustomCatch-all access to 'match-challenges' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.streaks.accesscustomCatch-all access to 'streaks' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.community_challenge_templates.accesscustomCatch-all access to 'community-challenge-templates' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.club_challenges.accesscustomCatch-all access to 'club-challenges' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.seasonal_quests.accesscustomCatch-all access to 'seasonal-quests' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.player_quests.accesscustomCatch-all access to 'player-quests' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.xp_source_configs.accesscustomCatch-all access to 'xp-source-configs' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.activity_xp.accesscustomCatch-all access to 'activity-xp' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.personal_bests.accesscustomCatch-all access to 'personal-bests' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.live_predictions.accesscustomCatch-all access to 'live-predictions' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.pickem_brackets.accesscustomCatch-all access to 'pickem-brackets' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.pickem_entries.accesscustomCatch-all access to 'pickem-entries' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.potm_polls.accesscustomCatch-all access to 'potm-polls' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.head_to_head.accesscustomCatch-all access to 'head-to-head' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.form_guides.accesscustomCatch-all access to 'form-guides' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.partnerships.accesscustomCatch-all access to 'partnerships' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.partner_search.accesscustomCatch-all access to 'partner-search' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.records.accesscustomCatch-all access to 'records' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.player_milestones.accesscustomCatch-all access to 'player-milestones' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.milestone_definitions.accesscustomCatch-all access to 'milestone-definitions' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.record_alerts.accesscustomCatch-all access to 'record-alerts' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.first_evers.accesscustomCatch-all access to 'first-evers' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.competition_record_books.accesscustomCatch-all access to 'competition-record-books' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.record_walls.accesscustomCatch-all access to 'record-walls' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.smart_recommendations.accesscustomCatch-all access to 'smart-recommendations' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.disciplines.accesscustomCatch-all access to 'disciplines' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.competition_formats.accesscustomCatch-all access to 'competition-formats' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.game_formats.accesscustomCatch-all access to 'game-formats' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.player_classes.accesscustomCatch-all access to 'player-classes' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.competition_classes.accesscustomCatch-all access to 'competition-classes' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.competition_levels.accesscustomCatch-all access to 'competition-levels' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.competition_types.accesscustomCatch-all access to 'competition-types' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.competition_config.accesscustomCatch-all access to 'competition-config' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.competitions.accesscustomCatch-all access to 'competitions' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.national_competition_structures.accesscustomCatch-all access to 'national-competition-structures' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.disability_categories.accesscustomCatch-all access to 'disability-categories' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.adaptive_rules.accesscustomCatch-all access to 'adaptive-rules' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.points_systems.accesscustomCatch-all access to 'points-systems' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.entry_fee_configs.accesscustomCatch-all access to 'entry-fee-configs' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.participant_limits.accesscustomCatch-all access to 'participant-limits' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.qualification_criteria.accesscustomCatch-all access to 'qualification-criteria' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.tournament_follows.accesscustomCatch-all access to 'tournament-follows' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.temporary_licenses.accesscustomCatch-all access to 'temporary-licenses' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.registrations.accesscustomCatch-all access to 'registrations' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.saved_tournaments.accesscustomCatch-all access to 'saved-tournaments' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.travel_intents.accesscustomCatch-all access to 'travel-intents' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.tournaments.accesscustomCatch-all access to 'tournaments' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.draws.accesscustomCatch-all access to 'draws' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.matches.accesscustomCatch-all access to 'matches' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.match_subscriptions.accesscustomCatch-all access to 'match-subscriptions' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.melee_tournaments.accesscustomCatch-all access to 'melee-tournaments' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.cup_formats.accesscustomCatch-all access to 'cup-formats' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.leagues.accesscustomCatch-all access to 'leagues' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.fixtures.accesscustomCatch-all access to 'fixtures' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.match_days.accesscustomCatch-all access to 'match-days' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.season_archives.accesscustomCatch-all access to 'season-archives' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.membership_categories.accesscustomCatch-all access to 'membership-categories' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.club_memberships.accesscustomCatch-all access to 'club-memberships' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.family_groups.accesscustomCatch-all access to 'family-groups' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.license_rules.accesscustomCatch-all access to 'license-rules' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.affiliation_records.accesscustomCatch-all access to 'affiliation-records' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.affiliation_applications.accesscustomCatch-all access to 'affiliation-applications' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.member_count_reports.accesscustomCatch-all access to 'member-count-reports' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.transfers.accesscustomCatch-all access to 'transfers' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.itc.accesscustomCatch-all access to 'itc' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.consent_records.accesscustomCatch-all access to 'consent-records' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.parental_consents.accesscustomCatch-all access to 'parental-consents' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.data_deletion_requests.accesscustomCatch-all access to 'data-deletion-requests' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.data_retention_policies.accesscustomCatch-all access to 'data-retention-policies' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.data_access_audit.accesscustomCatch-all access to 'data-access-audit' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.cookie_consent.accesscustomCatch-all access to 'cookie-consent' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.commissions.accesscustomCatch-all access to 'commissions' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.meetings.accesscustomCatch-all access to 'meetings' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.governance.accesscustomCatch-all access to 'governance' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.governance_meetings.accesscustomCatch-all access to 'governance-meetings' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.juries.accesscustomCatch-all access to 'juries' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.jury_decisions.accesscustomCatch-all access to 'jury-decisions' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.jury_reports.accesscustomCatch-all access to 'jury-reports' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.referee_delegates.accesscustomCatch-all access to 'referee-delegates' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.delegate_observations.accesscustomCatch-all access to 'delegate-observations' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.delegate_reports.accesscustomCatch-all access to 'delegate-reports' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.compliance_checklist_templates.accesscustomCatch-all access to 'compliance-checklist-templates' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.umpire_certifications.accesscustomCatch-all access to 'umpire-certifications' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.umpire_expenses.accesscustomCatch-all access to 'umpire-expenses' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.umpire_statistics.accesscustomCatch-all access to 'umpire-statistics' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.umpire_evaluations.accesscustomCatch-all access to 'umpire-evaluations' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.umpire_training_pathways.accesscustomCatch-all access to 'umpire-training-pathways' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.umpire_availability.accesscustomCatch-all access to 'umpire-availability' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.umpire_directory.accesscustomCatch-all access to 'umpire-directory' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.umpire_id_cards.accesscustomCatch-all access to 'umpire-id-cards' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.umpire_training_courses.accesscustomCatch-all access to 'umpire-training-courses' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.exam_questions.accesscustomCatch-all access to 'exam-questions' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.exam_sessions.accesscustomCatch-all access to 'exam-sessions' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.training_enrollments.accesscustomCatch-all access to 'training-enrollments' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.mentorship_programs.accesscustomCatch-all access to 'mentorship-programs' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.continuing_education.accesscustomCatch-all access to 'continuing-education' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.training_materials.accesscustomCatch-all access to 'training-materials' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.recertifications.accesscustomCatch-all access to 'recertifications' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.candidacy_records.accesscustomCatch-all access to 'candidacy-records' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.exam_definitions.accesscustomCatch-all access to 'exam-definitions' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.umpire_assignments.accesscustomCatch-all access to 'umpire-assignments' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.competition_umpire_requirements.accesscustomCatch-all access to 'competition-umpire-requirements' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.referee_teams.accesscustomCatch-all access to 'referee-teams' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.disciplinary_cases.accesscustomCatch-all access to 'disciplinary-cases' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.sanctions.accesscustomCatch-all access to 'sanctions' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.sanction_webhooks.accesscustomCatch-all access to 'sanction-webhooks' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.disciplinary_audit.accesscustomCatch-all access to 'disciplinary-audit' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.sanction_catalog.accesscustomCatch-all access to 'sanction-catalog' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.disciplinary_dashboard.accesscustomCatch-all access to 'disciplinary-dashboard' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.discipline.accesscustomCatch-all access to 'discipline' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.fipjp.accesscustomCatch-all access to 'fipjp' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.live_productions.accesscustomCatch-all access to 'live-productions' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.stream_recordings.accesscustomCatch-all access to 'stream-recordings' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.highlight_clips.accesscustomCatch-all access to 'highlight-clips' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.stream_embeds.accesscustomCatch-all access to 'stream-embeds' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.mene_summaries.accesscustomCatch-all access to 'mene-summaries' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.live_viewer.accesscustomCatch-all access to 'live-viewer' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.replay_requests.accesscustomCatch-all access to 'replay-requests' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.player_cards.accesscustomCatch-all access to 'player-cards' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.match_video_links.accesscustomCatch-all access to 'match-video-links' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.audio_only.accesscustomCatch-all access to 'audio-only' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.score_only_feed.accesscustomCatch-all access to 'score-only-feed' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.offline_recordings.accesscustomCatch-all access to 'offline-recordings' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.stream_schedules.accesscustomCatch-all access to 'stream-schedules' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.live_chat.accesscustomCatch-all access to 'live-chat' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.local_recordings.accesscustomCatch-all access to 'local-recordings' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.cloud_recordings.accesscustomCatch-all access to 'cloud-recordings' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.auto_highlight_configs.accesscustomCatch-all access to 'auto-highlight-configs' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.post_match_edits.accesscustomCatch-all access to 'post-match-edits' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.match_summary_videos.accesscustomCatch-all access to 'match-summary-videos' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.recording_shares.accesscustomCatch-all access to 'recording-shares' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.bulk_notifications.accesscustomCatch-all access to 'bulk-notifications' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.notification_webhooks.accesscustomCatch-all access to 'notification-webhooks' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.notification_delivery_events.accesscustomCatch-all access to 'notification-delivery-events' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.notification_statistics.accesscustomCatch-all access to 'notification-statistics' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.notification_digests.accesscustomCatch-all access to 'notification-digests' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.conversations.accesscustomCatch-all access to 'conversations' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.messages.accesscustomCatch-all access to 'messages' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.correspondences.accesscustomCatch-all access to 'correspondences' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.documents.accesscustomCatch-all access to 'documents' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.calendar_sync.accesscustomCatch-all access to 'calendar-sync' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.congresses.accesscustomCatch-all access to 'congresses' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.resolution_compliance.accesscustomCatch-all access to 'resolution-compliance' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.form_templates.accesscustomCatch-all access to 'form-templates' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.form_submissions.accesscustomCatch-all access to 'form-submissions' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.signature_requests.accesscustomCatch-all access to 'signature-requests' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.social_media.accesscustomCatch-all access to 'social-media' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.fee_schedules.accesscustomCatch-all access to 'fee-schedules' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.revenue_distribution_rules.accesscustomCatch-all access to 'revenue-distribution-rules' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.fee_management.accesscustomCatch-all access to 'fee-management' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.fee_surcharge_rules.accesscustomCatch-all access to 'fee-surcharge-rules' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.fee_surcharges.accesscustomCatch-all access to 'fee-surcharges' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.fee_waiver_rules.accesscustomCatch-all access to 'fee-waiver-rules' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.fee_waivers.accesscustomCatch-all access to 'fee-waivers' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.fee_splitting.accesscustomCatch-all access to 'fee-splitting' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.fee_audit.accesscustomCatch-all access to 'fee-audit' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.sanction_fee_configs.accesscustomCatch-all access to 'sanction-fee-configs' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.suspension_fee_configs.accesscustomCatch-all access to 'suspension-fee-configs' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.invoices.accesscustomCatch-all access to 'invoices' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.payments.accesscustomCatch-all access to 'payments' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.debts.accesscustomCatch-all access to 'debts' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.financial_reports.accesscustomCatch-all access to 'financial-reports' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.credit_notes.accesscustomCatch-all access to 'credit-notes' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.reminder_configs.accesscustomCatch-all access to 'reminder-configs' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.debt_collection.accesscustomCatch-all access to 'debt-collection' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.payment_gateways.accesscustomCatch-all access to 'payment-gateways' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.checkout.accesscustomCatch-all access to 'checkout' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.receipts.accesscustomCatch-all access to 'receipts' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.payment_history.accesscustomCatch-all access to 'payment-history' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.bank_transfers.accesscustomCatch-all access to 'bank-transfers' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.installment_plans.accesscustomCatch-all access to 'installment-plans' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.reconciliations.accesscustomCatch-all access to 'reconciliations' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.refunds.accesscustomCatch-all access to 'refunds' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.bulk_payments.accesscustomCatch-all access to 'bulk-payments' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.exchange_rates.accesscustomCatch-all access to 'exchange-rates' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.payment_audit.accesscustomCatch-all access to 'payment-audit' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.expense_claims.accesscustomCatch-all access to 'expense-claims' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.expenses.accesscustomCatch-all access to 'expenses' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.expense_reports.accesscustomCatch-all access to 'expense-reports' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.accounts.accesscustomCatch-all access to 'accounts' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.accounting_entries.accesscustomCatch-all access to 'accounting-entries' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.budgets.accesscustomCatch-all access to 'budgets' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.accounting_export.accesscustomCatch-all access to 'accounting-export' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.accounting_integrations.accesscustomCatch-all access to 'accounting-integrations' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.financial_audit_log.accesscustomCatch-all access to 'financial-audit-log' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.accounting_reports.accesscustomCatch-all access to 'accounting-reports' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.chart_of_accounts_mappings.accesscustomCatch-all access to 'chart-of-accounts-mappings' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.period_locks.accesscustomCatch-all access to 'period-locks' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.financial_statements.accesscustomCatch-all access to 'financial-statements' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.financial_reporting.accesscustomCatch-all access to 'financial-reporting' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.bank_reconciliation.accesscustomCatch-all access to 'bank-reconciliation' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.legal.accesscustomCatch-all access to 'legal' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.groups.accesscustomCatch-all access to 'groups' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.license_fee_revisions.accesscustomCatch-all access to 'license-fee-revisions' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.invoice_number_series.accesscustomCatch-all access to 'invoice-number-series' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.payment_term_rules.accesscustomCatch-all access to 'payment-term-rules' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.sepa_mandates.accesscustomCatch-all access to 'sepa-mandates' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.sepa_direct_debits.accesscustomCatch-all access to 'sepa-direct-debits' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.payment_method_policies.accesscustomCatch-all access to 'payment-method-policies' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.multi_currency_reports.accesscustomCatch-all access to 'multi-currency-reports' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.coach_qualifications.accesscustomCatch-all access to 'coach-qualifications' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.coach_certifications.accesscustomCatch-all access to 'coach-certifications' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.training_programs.accesscustomCatch-all access to 'training-programs' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.coach_course_registrations.accesscustomCatch-all access to 'coach-course-registrations' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.coaching_materials.accesscustomCatch-all access to 'coaching-materials' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.player_developments.accesscustomCatch-all access to 'player-developments' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.certification_renewals.accesscustomCatch-all access to 'certification-renewals' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.development_plans.accesscustomCatch-all access to 'development-plans' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.coach_assignments.accesscustomCatch-all access to 'coach-assignments' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.coaching_sessions.accesscustomCatch-all access to 'coaching-sessions' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.program_enrollments.accesscustomCatch-all access to 'program-enrollments' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.training_attendance.accesscustomCatch-all access to 'training-attendance' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.video_analyses.accesscustomCatch-all access to 'video-analyses' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.drill_library.accesscustomCatch-all access to 'drill-library' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.coaching.accesscustomCatch-all access to 'coaching' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.e_learning.accesscustomCatch-all access to 'e-learning' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.venues.accesscustomCatch-all access to 'venues' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.buvette.accesscustomCatch-all access to 'buvette' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.tue_applications.accesscustomCatch-all access to 'tue-applications' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.doping_tests.accesscustomCatch-all access to 'doping-tests' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.medical_staff.accesscustomCatch-all access to 'medical-staff' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.injury_reports.accesscustomCatch-all access to 'injury-reports' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.wada_code_articles.accesscustomCatch-all access to 'wada-code-articles' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.wada_compliance_records.accesscustomCatch-all access to 'wada-compliance-records' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.whereabouts.accesscustomCatch-all access to 'whereabouts' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.whereabouts_missed_tests.accesscustomCatch-all access to 'whereabouts-missed-tests' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.testing_pools.accesscustomCatch-all access to 'testing-pools' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.testing_schedule.accesscustomCatch-all access to 'testing-schedule' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.chain_of_custody.accesscustomCatch-all access to 'chain-of-custody' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.b_sample_analyses.accesscustomCatch-all access to 'b-sample-analyses' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.test_result_notifications.accesscustomCatch-all access to 'test-result-notifications' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.tue_committee_reviews.accesscustomCatch-all access to 'tue-committee-reviews' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.adrv_cases.accesscustomCatch-all access to 'adrv-cases' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.adrv_sanctions.accesscustomCatch-all access to 'adrv-sanctions' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.education_courses.accesscustomCatch-all access to 'education-courses' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.education_completions.accesscustomCatch-all access to 'education-completions' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.prohibited_substances.accesscustomCatch-all access to 'prohibited-substances' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.doping_audit.accesscustomCatch-all access to 'doping-audit' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.disability_classifications.accesscustomCatch-all access to 'disability-classifications' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.para.accesscustomCatch-all access to 'para' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.emergency_contacts.accesscustomCatch-all access to 'emergency-contacts' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.player_medical_info.accesscustomCatch-all access to 'player-medical-info' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.international_health_certificates.accesscustomCatch-all access to 'international-health-certificates' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.tue_medical_details.accesscustomCatch-all access to 'tue-medical-details' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.tue_review_boards.accesscustomCatch-all access to 'tue-review-boards' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.tue_board_decisions.accesscustomCatch-all access to 'tue-board-decisions' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.tue_player_status.accesscustomCatch-all access to 'tue-player-status' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.nado_referrals.accesscustomCatch-all access to 'nado-referrals' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.tue_renewals.accesscustomCatch-all access to 'tue-renewals' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.safety_checklists.accesscustomCatch-all access to 'safety-checklists' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.safety_checklist_completions.accesscustomCatch-all access to 'safety-checklist-completions' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.safety_incidents.accesscustomCatch-all access to 'safety-incidents' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.first_aid_requirements.accesscustomCatch-all access to 'first-aid-requirements' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.weather_policies.accesscustomCatch-all access to 'weather-policies' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.safeguarding_incidents.accesscustomCatch-all access to 'safeguarding-incidents' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.safeguarding_officer_directory.accesscustomCatch-all access to 'safeguarding-officer-directory' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.weather.accesscustomCatch-all access to 'weather' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.background_checks.accesscustomCatch-all access to 'background-checks' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.safeguarding_reports.accesscustomCatch-all access to 'safeguarding-reports' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.safeguarding_policies.accesscustomCatch-all access to 'safeguarding-policies' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.medical_emergency_incidents.accesscustomCatch-all access to 'medical-emergency-incidents' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.first_aid_compliance.accesscustomCatch-all access to 'first-aid-compliance' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.medical_staff_registrations.accesscustomCatch-all access to 'medical-staff-registrations' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.weather_alerts.accesscustomCatch-all access to 'weather-alerts' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.integrity_alerts.accesscustomCatch-all access to 'integrity-alerts' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.suspicious_activity_reports.accesscustomCatch-all access to 'suspicious-activity-reports' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.integrity_investigations.accesscustomCatch-all access to 'integrity-investigations' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.event_equipment_checks.accesscustomCatch-all access to 'event-equipment-checks' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.equipment_confiscations.accesscustomCatch-all access to 'equipment-confiscations' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.betting_integrity_bodies.accesscustomCatch-all access to 'betting-integrity-bodies' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.betting_integrity_notifications.accesscustomCatch-all access to 'betting-integrity-notifications' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.integrity_audit_log.accesscustomCatch-all access to 'integrity-audit-log' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.disputes.accesscustomCatch-all access to 'disputes' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.dispute_precedents.accesscustomCatch-all access to 'dispute-precedents' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.dispute_appeals.accesscustomCatch-all access to 'dispute-appeals' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.dispute_audit_log.accesscustomCatch-all access to 'dispute-audit-log' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.safeguarding_training_requirements.accesscustomCatch-all access to 'safeguarding-training-requirements' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.safeguarding_training_records.accesscustomCatch-all access to 'safeguarding-training-records' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.safeguarding_training.accesscustomCatch-all access to 'safeguarding-training' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.youth_event_risk_assessments.accesscustomCatch-all access to 'youth-event-risk-assessments' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.adult_child_ratio_requirements.accesscustomCatch-all access to 'adult-child-ratio-requirements' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.adult_child_ratio_checks.accesscustomCatch-all access to 'adult-child-ratio-checks' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.safeguarding_complaints.accesscustomCatch-all access to 'safeguarding-complaints' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.safeguarding_audit.accesscustomCatch-all access to 'safeguarding-audit' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.gender_equity_reports.accesscustomCatch-all access to 'gender-equity-reports' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.adaptive_equipment.accesscustomCatch-all access to 'adaptive-equipment' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.inclusion_policies.accesscustomCatch-all access to 'inclusion-policies' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.diversity_snapshots.accesscustomCatch-all access to 'diversity-snapshots' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.venue_accessibility_audits.accesscustomCatch-all access to 'venue-accessibility-audits' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.inclusion_audit.accesscustomCatch-all access to 'inclusion-audit' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.encrypted_reports.accesscustomCatch-all access to 'encrypted-reports' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.whistleblower_reports.accesscustomCatch-all access to 'whistleblower-reports' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.retaliation_reports.accesscustomCatch-all access to 'retaliation-reports' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.background_check_requirements.accesscustomCatch-all access to 'background-check-requirements' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.background_check_compliance.accesscustomCatch-all access to 'background-check-compliance' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.nation_safeguarding_policies.accesscustomCatch-all access to 'nation-safeguarding-policies' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.safeguarding_officers.accesscustomCatch-all access to 'safeguarding-officers' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.coach_safeguarding_training.accesscustomCatch-all access to 'coach-safeguarding-training' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.confidential_safeguarding_incidents.accesscustomCatch-all access to 'confidential-safeguarding-incidents' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.sponsors.accesscustomCatch-all access to 'sponsors' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.grants.accesscustomCatch-all access to 'grants' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.sponsor_allocations.accesscustomCatch-all access to 'sponsor-allocations' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.marketplace.accesscustomCatch-all access to 'marketplace' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.broadcast.accesscustomCatch-all access to 'broadcast' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.squad_travel.accesscustomCatch-all access to 'squad-travel' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.vip.accesscustomCatch-all access to 'vip' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.wedding.accesscustomCatch-all access to 'wedding' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.restricted_facility.accesscustomCatch-all access to 'restricted-facility' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.forums.accesscustomCatch-all access to 'forums' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.ad_placements.accesscustomCatch-all access to 'ad-placements' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.retailers.accesscustomCatch-all access to 'retailers' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.boule_bars.accesscustomCatch-all access to 'boule-bars' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.federation_shops.accesscustomCatch-all access to 'federation-shops' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.shop_products.accesscustomCatch-all access to 'shop-products' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.marketplace_watchlist.accesscustomCatch-all access to 'marketplace-watchlist' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.seller_ratings.accesscustomCatch-all access to 'seller-ratings' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.classified_ads.accesscustomCatch-all access to 'classified-ads' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.manufacturers.accesscustomCatch-all access to 'manufacturers' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.bulk_orders.accesscustomCatch-all access to 'bulk-orders' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.marketplace_payments.accesscustomCatch-all access to 'marketplace-payments' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.marketplace_payouts.accesscustomCatch-all access to 'marketplace-payouts' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.shipments.accesscustomCatch-all access to 'shipments' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.seller_profiles.accesscustomCatch-all access to 'seller-profiles' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.seller_products.accesscustomCatch-all access to 'seller-products' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.seller_orders.accesscustomCatch-all access to 'seller-orders' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.stripe_connect.accesscustomCatch-all access to 'stripe-connect' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.seller_reviews.accesscustomCatch-all access to 'seller-reviews' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.return_requests.accesscustomCatch-all access to 'return-requests' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.stock_movements.accesscustomCatch-all access to 'stock-movements' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.low_stock_alerts.accesscustomCatch-all access to 'low-stock-alerts' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.sponsorship_opportunities.accesscustomCatch-all access to 'sponsorship-opportunities' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.sponsor_profiles.accesscustomCatch-all access to 'sponsor-profiles' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.ad_placement_analytics.accesscustomCatch-all access to 'ad-placement-analytics' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.sponsor_visibility.accesscustomCatch-all access to 'sponsor-visibility' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.sponsorship_packages.accesscustomCatch-all access to 'sponsorship-packages' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.sponsor_matches.accesscustomCatch-all access to 'sponsor-matches' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.homologation_applications.accesscustomCatch-all access to 'homologation-applications' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.certification_tests.accesscustomCatch-all access to 'certification-tests' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.approved_equipment.accesscustomCatch-all access to 'approved-equipment' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.homologation_renewals.accesscustomCatch-all access to 'homologation-renewals' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.equipment_inspections.accesscustomCatch-all access to 'equipment-inspections' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.prize_pools.accesscustomCatch-all access to 'prize-pools' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.prize_payouts.accesscustomCatch-all access to 'prize-payouts' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.trophy_inventories.accesscustomCatch-all access to 'trophy-inventories' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.trophy_awards.accesscustomCatch-all access to 'trophy-awards' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.youth_academies.accesscustomCatch-all access to 'youth-academies' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.youth_players.accesscustomCatch-all access to 'youth-players' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.youth_competition_pathways.accesscustomCatch-all access to 'youth-competition-pathways' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.school_partnerships.accesscustomCatch-all access to 'school-partnerships' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.youth_progress_reports.accesscustomCatch-all access to 'youth-progress-reports' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.school_programs.accesscustomCatch-all access to 'school-programs' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.junior_competitions.accesscustomCatch-all access to 'junior-competitions' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.talent_programs.accesscustomCatch-all access to 'talent-programs' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.national_youth_programs.accesscustomCatch-all access to 'national-youth-programs' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.junior_coach_resources.accesscustomCatch-all access to 'junior-coach-resources' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.junior_statistics.accesscustomCatch-all access to 'junior-statistics' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.training_entries.accesscustomCatch-all access to 'training-entries' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.development_goals.accesscustomCatch-all access to 'development-goals' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.goal_templates.accesscustomCatch-all access to 'goal-templates' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.player_pathways.accesscustomCatch-all access to 'player-pathways' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.ranking_configs.accesscustomCatch-all access to 'ranking-configs' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.ranking_entries.accesscustomCatch-all access to 'ranking-entries' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.rankings.accesscustomCatch-all access to 'rankings' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.club_ranking_configs.accesscustomCatch-all access to 'club-ranking-configs' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.club_ranking_entries.accesscustomCatch-all access to 'club-ranking-entries' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.club_rankings.accesscustomCatch-all access to 'club-rankings' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.ranking_disputes.accesscustomCatch-all access to 'ranking-disputes' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.cross_tenant_elo.accesscustomCatch-all access to 'cross-tenant-elo' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.ranking_presets.accesscustomCatch-all access to 'ranking-presets' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.user_ranking_presets.accesscustomCatch-all access to 'user-ranking-presets' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.elo.accesscustomCatch-all access to 'elo' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.major_events.accesscustomCatch-all access to 'major-events' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.event_tickets.accesscustomCatch-all access to 'event-tickets' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.delegation_registrations.accesscustomCatch-all access to 'delegation-registrations' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.media_accreditations.accesscustomCatch-all access to 'media-accreditations' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.volunteer_registrations.accesscustomCatch-all access to 'volunteer-registrations' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.merchandise_preorders.accesscustomCatch-all access to 'merchandise-preorders' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.corporate_events.accesscustomCatch-all access to 'corporate-events' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.event_packages.accesscustomCatch-all access to 'event-packages' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.heritage_content.accesscustomCatch-all access to 'heritage-content' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.hall_of_fame.accesscustomCatch-all access to 'hall-of-fame' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.historic_competitions.accesscustomCatch-all access to 'historic-competitions' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.museums.accesscustomCatch-all access to 'museums' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.cultural_events.accesscustomCatch-all access to 'cultural-events' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.training_camps.accesscustomCatch-all access to 'training-camps' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.travel_packages.accesscustomCatch-all access to 'travel-packages' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.destination_guides.accesscustomCatch-all access to 'destination-guides' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.travel_companions.accesscustomCatch-all access to 'travel-companions' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.club_exchanges.accesscustomCatch-all access to 'club-exchanges' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.accommodation_listings.accesscustomCatch-all access to 'accommodation-listings' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.world_championships.accesscustomCatch-all access to 'world-championships' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.continental_championships.accesscustomCatch-all access to 'continental-championships' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.event_bids.accesscustomCatch-all access to 'event-bids' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.event_timelines.accesscustomCatch-all access to 'event-timelines' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.event_venue_approvals.accesscustomCatch-all access to 'event-venue-approvals' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.delegation_accommodations.accesscustomCatch-all access to 'delegation-accommodations' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.event_transport_plans.accesscustomCatch-all access to 'event-transport-plans' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.event_accreditations.accesscustomCatch-all access to 'event-accreditations' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.event_ceremonies.accesscustomCatch-all access to 'event-ceremonies' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.event_vip_hospitality.accesscustomCatch-all access to 'event-vip-hospitality' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.event_budgets.accesscustomCatch-all access to 'event-budgets' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.event_tenant.accesscustomCatch-all access to 'event-tenant' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.elections.accesscustomCatch-all access to 'elections' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.statistics.accesscustomCatch-all access to 'statistics' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.rules.accesscustomCatch-all access to 'rules' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.rules_education.accesscustomCatch-all access to 'rules-education' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.developer.accesscustomCatch-all access to 'developer' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.v1.accesscustomCatch-all access to 'v1' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.i18n.accesscustomCatch-all access to 'i18n' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.content.accesscustomCatch-all access to 'content' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.broadcast_templates.accesscustomCatch-all access to 'broadcast-templates' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.print.accesscustomCatch-all access to 'print' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.report_templates.accesscustomCatch-all access to 'report-templates' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.federation_reports.accesscustomCatch-all access to 'federation-reports' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.report_deadlines.accesscustomCatch-all access to 'report-deadlines' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.compliance_checklists.accesscustomCatch-all access to 'compliance-checklists' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.data_breaches.accesscustomCatch-all access to 'data-breaches' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.sensitive_fields.accesscustomCatch-all access to 'sensitive-fields' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.selection_processes.accesscustomCatch-all access to 'selection-processes' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.national_team_records.accesscustomCatch-all access to 'national-team-records' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.qualification_pathways.accesscustomCatch-all access to 'qualification-pathways' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.qualification_entries.accesscustomCatch-all access to 'qualification-entries' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.qualification_cross_references.accesscustomCatch-all access to 'qualification-cross-references' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.international_competitions.accesscustomCatch-all access to 'international-competitions' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.nation_ceremony_info.accesscustomCatch-all access to 'nation-ceremony-info' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.hosting_bids.accesscustomCatch-all access to 'hosting-bids' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.national_ranking_feedback.accesscustomCatch-all access to 'national-ranking-feedback' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.competition_awards.accesscustomCatch-all access to 'competition-awards' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.season_awards.accesscustomCatch-all access to 'season-awards' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.award_hall_of_fame.accesscustomCatch-all access to 'award-hall-of-fame' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.championship_series.accesscustomCatch-all access to 'championship-series' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.news_articles.accesscustomCatch-all access to 'news-articles' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.hotel_bookings.accesscustomCatch-all access to 'hotel-bookings' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.travel_schedules.accesscustomCatch-all access to 'travel-schedules' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.transfer_bookings.accesscustomCatch-all access to 'transfer-bookings' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.travel_coordinators.accesscustomCatch-all access to 'travel-coordinators' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.travel_expenses.accesscustomCatch-all access to 'travel-expenses' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.feature_requests.accesscustomCatch-all access to 'feature-requests' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.startup.accesscustomCatch-all access to 'startup' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.observability.accesscustomCatch-all access to 'observability' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.incidents.accesscustomCatch-all access to 'incidents' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.communications.accesscustomCatch-all access to 'communications' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.federation_integrations.accesscustomCatch-all access to 'federation-integrations' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.fipjp_submissions.accesscustomCatch-all access to 'fipjp-submissions' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.continental_exchanges.accesscustomCatch-all access to 'continental-exchanges' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.payment_gateway_integrations.accesscustomCatch-all access to 'payment-gateway-integrations' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.calendar_integrations.accesscustomCatch-all access to 'calendar-integrations' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.social_media_integrations.accesscustomCatch-all access to 'social-media-integrations' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.streaming_integrations.accesscustomCatch-all access to 'streaming-integrations' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.mapping_integrations.accesscustomCatch-all access to 'mapping-integrations' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.wada_adams_integrations.accesscustomCatch-all access to 'wada-adams-integrations' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.insurance_integrations.accesscustomCatch-all access to 'insurance-integrations' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.gov_sports_registry_integrations.accesscustomCatch-all access to 'gov-sports-registry-integrations' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.export_jobs.accesscustomCatch-all access to 'export-jobs' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.scheduled_exports.accesscustomCatch-all access to 'scheduled-exports' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.export_templates.accesscustomCatch-all access to 'export-templates' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.competition_exports.accesscustomCatch-all access to 'competition-exports' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.widgets.accesscustomCatch-all access to 'widgets' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.stripe.accesscustomCatch-all access to 'stripe' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.paypal.accesscustomCatch-all access to 'paypal' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.swish.accesscustomCatch-all access to 'swish' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.ideal.accesscustomCatch-all access to 'ideal' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.bancontact.accesscustomCatch-all access to 'bancontact' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.bizum.accesscustomCatch-all access to 'bizum' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.sepa_direct_debit.accesscustomCatch-all access to 'sepa-direct-debit' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.klarna.accesscustomCatch-all access to 'klarna' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.accounting.accesscustomCatch-all access to 'accounting' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.geo.accesscustomCatch-all access to 'geo' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.anti_doping.accesscustomCatch-all access to 'anti-doping' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.communication.accesscustomCatch-all access to 'communication' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.youtube_embeds.accesscustomCatch-all access to 'youtube-embeds' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.twitch_embeds.accesscustomCatch-all access to 'twitch-embeds' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.rtmp_relays.accesscustomCatch-all access to 'rtmp-relays' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.stream_metadata.accesscustomCatch-all access to 'stream-metadata' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.vimeo_replays.accesscustomCatch-all access to 'vimeo-replays' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.cloudflare_streams.accesscustomCatch-all access to 'cloudflare-streams' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.dns.accesscustomCatch-all access to 'dns' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.analytics.accesscustomCatch-all access to 'analytics' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.mobile.accesscustomCatch-all access to 'mobile' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.offline.accesscustomCatch-all access to 'offline' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.live_scores.accesscustomCatch-all access to 'live-scores' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.match_timelines.accesscustomCatch-all access to 'match-timelines' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.court_views.accesscustomCatch-all access to 'court-views' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.follows.accesscustomCatch-all access to 'follows' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.commentary.accesscustomCatch-all access to 'commentary' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.score_alerts.accesscustomCatch-all access to 'score-alerts' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.multi_match.accesscustomCatch-all access to 'multi-match' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.community.accesscustomCatch-all access to 'community' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.push.accesscustomCatch-all access to 'push' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.onboarding.accesscustomCatch-all access to 'onboarding' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.data_visibility.accesscustomCatch-all access to 'data-visibility' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.ar.accesscustomCatch-all access to 'ar' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.sri.accesscustomCatch-all access to 'sri' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.chain.accesscustomCatch-all access to 'chain' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.inquiries.accesscustomCatch-all access to 'inquiries' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.inquiry_templates.accesscustomCatch-all access to 'inquiry-templates' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.scores.accesscustomCatch-all access to 'scores' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.operator.accesscustomCatch-all access to 'operator' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.federation.accesscustomCatch-all access to 'federation' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.ministry_reports.accesscustomCatch-all access to 'ministry-reports' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.officials.accesscustomCatch-all access to 'officials' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.mentorship.accesscustomCatch-all access to 'mentorship' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.finance.accesscustomCatch-all access to 'finance' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.staff.accesscustomCatch-all access to 'staff' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.unsubscribe.accesscustomCatch-all access to 'unsubscribe' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.referee_loans.accesscustomCatch-all access to 'referee-loans' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.referee_assignments.accesscustomCatch-all access to 'referee-assignments' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.referee_payouts.accesscustomCatch-all access to 'referee-payouts' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.subscriptions.accesscustomCatch-all access to 'subscriptions' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.scoreboards.accesscustomCatch-all access to 'scoreboards' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.orgnodes.accesscustomCatch-all access to 'orgnodes' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.scoreboard_kits.accesscustomCatch-all access to 'scoreboard-kits' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.scoreboard_kit_instances.accesscustomCatch-all access to 'scoreboard-kit-instances' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.scoreboard_rentals.accesscustomCatch-all access to 'scoreboard-rentals' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.referee_calls.accesscustomCatch-all access to 'referee-calls' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.events.accesscustomCatch-all access to 'events' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.surveys.accesscustomCatch-all access to 'surveys' domain endpoints. Narrower capabilities may be introduced over time to replace this.
domain.survey_templates.accesscustomCatch-all access to 'survey-templates' domain endpoints. Narrower capabilities may be introduced over time to replace this.

Error Handling

Authentication Errors

HTTPerrorMeaningAction
400invalid_requestMissing/malformed parametersCheck grant_type, client_assertion_type
401invalid_clientUnknown client_id, kid mismatch, signature failure, alg mismatchCheck key, kid, client_id
400invalid_grantExpired/replay/wrong audience/wrong issuerCheck iat/exp, aud, iss, jti
400invalid_scopeRequested capability unknown or not assigned to clientCheck client's capabilities in Admin UI
401invalid_dpop_proofDPoP header missing or malformedSee DPoP section
403missing_capabilitiesToken lacks capability for this endpointRequest token with correct scope
429Rate limit / lockout after repeated failuresWait per Retry-After header
503server_errorAuth service unavailableRetry with exponential backoff

Example Error Responses

// 400 invalid_request
{
  "error": "invalid_request",
  "error_description": "client_assertion_type must be 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer'"
}

// 401 invalid_client
{
  "error": "invalid_client",
  "error_description": "No active key with kid '2026-04-expired' found for client 'acme-integration'"
}

// 400 invalid_scope
{
  "error": "invalid_scope",
  "error_description": "Client 'acme-integration' does not have capability 'admin:delete'"
}

// 403 missing_capabilities (on API call, not token request)
{
  "detail": {
    "error": "missing_capabilities",
    "missing": ["bookings:create"]
  }
}

General API Errors

CodeMeaningWhen
400Bad RequestInvalid JSON in where, malformed input
401UnauthorizedMissing or invalid token
404Not FoundResource doesn't exist or belongs to another tenant
409ConflictCascade delete denied — children exist
412Precondition FailedETag mismatch — document modified by someone else
422Validation ErrorRequest body fails schema validation
428Precondition RequiredMissing If-Match header on update/delete
429Too Many RequestsRate limit exceeded
# Recommended error handling
try:
    product, etag = api.get("products", "nonexistent-id")
except requests.HTTPError as e:
    status = e.response.status_code
    body = e.response.json()

    if status == 401:
        print("Token expired or invalid — request a new one")
    elif status == 403:
        missing = body.get("detail", {}).get("missing", [])
        print(f"Missing capabilities: {missing}")
    elif status == 404:
        print("Not found")
    elif status == 412:
        print("ETag conflict — re-fetch and retry")
    elif status == 429:
        retry_after = e.response.headers.get("Retry-After", "60")
        print(f"Rate limited — retry after {retry_after}s")
    else:
        print(f"Error {status}: {body}")
try
{
    var (product, etag) = await api.GetAsync("products", "nonexistent");
}
catch (HttpRequestException ex)
{
    Console.WriteLine($"HTTP {(int?)ex.StatusCode}: {ex.Message}");
    switch (ex.StatusCode)
    {
        case System.Net.HttpStatusCode.Unauthorized:
            Console.WriteLine("Token expired — request a new one");
            break;
        case System.Net.HttpStatusCode.Forbidden:
            Console.WriteLine("Missing capabilities");
            break;
        case System.Net.HttpStatusCode.NotFound:
            Console.WriteLine("Resource not found");
            break;
    }
}
const resp = await fetch(`${baseUrl}/products/nonexistent`, { headers })
if (!resp.ok) {
  const body = await resp.json()
  switch (resp.status) {
    case 401: console.error('Token expired'); break
    case 403: console.error('Missing capabilities:', body.detail?.missing); break
    case 404: console.error('Not found'); break
    case 429:
      console.error(`Rate limited, retry after ${resp.headers.get('Retry-After')}s`)
      break
    default: console.error(`Error ${resp.status}:`, body)
  }
}
# Check HTTP status code
curl -s -o response.json -w '%{http_code}' 'http://api.petanque.life/products/nonexistent' \
  -H 'Authorization: Bearer eyJ...'

cat response.json | jq .

Rate Limiting

Endpoints may have per-route rate limits. When exceeded, the API returns 429 Too Many Requests with a Retry-After header.

ContextLimitLockout
M2M token requestsPer-client, per-IP5 consecutive failures triggers 15-minute lockout
API endpointsPer-route (configurable)429 with Retry-After
# Respect Retry-After
import time

resp = requests.get(f"{base_url}/bookings", headers=headers)
if resp.status_code == 429:
    retry_after = int(resp.headers.get("Retry-After", 60))
    time.sleep(retry_after)
    resp = requests.get(f"{base_url}/bookings", headers=headers)

Key Rotation

Rotate keys without downtime using the overlap period.

1
Create New Key
Admin creates a new key in UI or via POST /api-clients/{id}/keys/rotate.
2
Overlap Period Starts
Old key gets active_until = now + 30 days. Both keys are valid.
3
Switch to New Key
Update your client to use the new kid immediately.
4
Wait
During the overlap, both old and new keys work. Admin UI shows countdown.
5
Old Key Expires
After 30 days, the old key is removed automatically.

Automatic Rotation in Code

import json

# Check key status via API
resp = requests.get(f"{base_url}/api-clients/{client_id}",
                    headers=admin_headers)
client_data = resp.json()

for key in client_data.get("keys", []):
    kid = key["kid"]
    active_until = key.get("active_until")
    if active_until:
        print(f"Key {kid} expires: {active_until}")
        # Plan rotation before this date
    else:
        print(f"Key {kid} is the primary key (no expiry)")

# Best practice: load kid from config, not hardcoded
# When you deploy a new key, update your config to use the new kid

Python SDK Skeleton

Complete working example with automatic token management.

"""
Complete M2M integration example.
pip install authlib requests
"""
from authlib.jose import jwt as jose_jwt
from datetime import datetime, timedelta, UTC
import uuid, time, requests

BASE = "http://api.petanque.life"
CLIENT_ID = "acme-integration"
KID = "2026-04-primary"
TOKEN_ENDPOINT = f"{BASE}/auth/token"

with open("client-private-key.pem", "rb") as f:
    PRIVATE_KEY = f.read()


def get_access_token(scope: str = "") -> tuple[str, int]:
    now = datetime.now(UTC)
    assertion = jose_jwt.encode(
        header={"alg": "ES256", "kid": KID},
        payload={
            "iss": CLIENT_ID, "sub": CLIENT_ID,
            "aud": TOKEN_ENDPOINT,
            "jti": str(uuid.uuid4()),
            "iat": int(now.timestamp()),
            "exp": int((now + timedelta(minutes=2)).timestamp()),
        },
        key=PRIVATE_KEY,
    ).decode()

    resp = requests.post(TOKEN_ENDPOINT, data={
        "grant_type": "client_credentials",
        "client_id": CLIENT_ID,
        "client_assertion_type":
            "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
        "client_assertion": assertion,
        "scope": scope,
    })

    if resp.status_code != 200:
        error = resp.json()
        raise RuntimeError(
            f"Token error: {error['error']} — {error.get('error_description', '')}"
        )

    data = resp.json()
    return data["access_token"], data["expires_in"]


def main():
    token, expires_in = get_access_token("bookings:read bookings:create")
    print(f"Got token (expires in {expires_in}s)")

    headers = {"Authorization": f"Bearer {token}"}

    # List bookings
    resp = requests.get(f"{BASE}/bookings/", headers=headers)
    resp.raise_for_status()
    bookings = resp.json()
    print(f"Found {bookings['total']} bookings")

    for b in bookings["items"]:
        print(f"  {b['_id']} — {b.get('status', 'unknown')}")


if __name__ == "__main__":
    main()

C# SDK Skeleton

Complete working example using .NET 8+.

// NuGet packages:
// - Microsoft.IdentityModel.Tokens
// - System.IdentityModel.Tokens.Jwt

using System.IdentityModel.Tokens.Jwt;
using System.Net.Http.Json;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Text.Json;
using Microsoft.IdentityModel.Tokens;

const string BaseUrl = "http://api.petanque.life";
const string ClientId = "acme-integration";
const string Kid = "2026-04-primary";
var tokenEndpoint = $"{BaseUrl}/auth/token";

// Load key
var ecdsa = ECDsa.Create();
ecdsa.ImportFromPem(File.ReadAllText("client-private-key.pem"));

// Get access token
var handler = new JwtSecurityTokenHandler();
var assertion = handler.CreateEncodedJwt(new SecurityTokenDescriptor {
    Issuer = ClientId,
    Subject = new ClaimsIdentity(new[] {
        new Claim("sub", ClientId),
        new Claim("jti", Guid.NewGuid().ToString()),
    }),
    Audience = tokenEndpoint,
    IssuedAt = DateTime.UtcNow,
    Expires = DateTime.UtcNow.AddMinutes(2),
    SigningCredentials = new SigningCredentials(
        new ECDsaSecurityKey(ecdsa) { KeyId = Kid },
        SecurityAlgorithms.EcdsaSha256),
});

using var http = new HttpClient();
var tokenResp = await http.PostAsync(tokenEndpoint, new FormUrlEncodedContent(new[] {
    KeyValuePair.Create("grant_type", "client_credentials"),
    KeyValuePair.Create("client_id", ClientId),
    KeyValuePair.Create("client_assertion_type",
        "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"),
    KeyValuePair.Create("client_assertion", assertion),
    KeyValuePair.Create("scope", "bookings:read"),
}));
tokenResp.EnsureSuccessStatusCode();
var tokenData = await tokenResp.Content.ReadFromJsonAsync<JsonElement>();
var accessToken = tokenData.GetProperty("access_token").GetString()!;
Console.WriteLine($"Expires in: {tokenData.GetProperty("expires_in")}s");

// Call API
http.DefaultRequestHeaders.Add("Authorization", $"Bearer {accessToken}");
var bookings = await http.GetFromJsonAsync<JsonElement>($"{BaseUrl}/bookings/");
Console.WriteLine($"Found {bookings.GetProperty("total")} bookings");

Node SDK Skeleton

Complete working example using ES modules.

// npm install jose
import { importPKCS8, SignJWT } from 'jose'
import { readFile } from 'fs/promises'

const BASE = 'http://api.petanque.life'
const CLIENT_ID = 'acme-integration'
const KID = '2026-04-primary'
const TOKEN_ENDPOINT = `${BASE}/auth/token`

const privateKey = await importPKCS8(
  await readFile('client-private-key.pem', 'utf8'),
  'ES256'
)

// Get access token
const assertion = await new SignJWT({
  sub: CLIENT_ID,
  jti: crypto.randomUUID(),
})
  .setProtectedHeader({ alg: 'ES256', kid: KID })
  .setIssuer(CLIENT_ID)
  .setAudience(TOKEN_ENDPOINT)
  .setIssuedAt()
  .setExpirationTime('2m')
  .sign(privateKey)

const tokenResp = await fetch(TOKEN_ENDPOINT, {
  method: 'POST',
  headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
  body: new URLSearchParams({
    grant_type: 'client_credentials',
    client_id: CLIENT_ID,
    client_assertion_type: 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer',
    client_assertion: assertion,
    scope: 'bookings:read',
  }),
})

if (!tokenResp.ok) {
  const err = await tokenResp.json()
  console.error(`Token error: ${err.error} — ${err.error_description}`)
  process.exit(1)
}

const { access_token, expires_in } = await tokenResp.json()
console.log(`Token expires in ${expires_in}s`)

// Call API
const bookings = await fetch(`${BASE}/bookings/`, {
  headers: { Authorization: `Bearer ${access_token}` },
}).then(r => r.json())

console.log(`Found ${bookings.total} bookings`)
for (const b of bookings.items) {
  console.log(`  ${b._id} — ${b.status ?? 'unknown'}`)
}

cURL Workflow

Complete bash script for testing and debugging. Requires: openssl, jq, curl.

#!/bin/bash
set -euo pipefail

CLIENT_ID="acme-integration"
KID="2026-04-primary"
TOKEN_ENDPOINT="http://api.petanque.life/auth/token"
PRIVATE_KEY="client-private-key.pem"

# --- Build JWT ---
HEADER=$(echo -n '{"alg":"ES256","kid":"'$KID'","typ":"JWT"}' \
  | openssl base64 -e -A | tr '+/' '-_' | tr -d '=')

NOW=$(date +%s)
EXP=$((NOW + 120))
JTI=$(cat /proc/sys/kernel/random/uuid 2>/dev/null || uuidgen)
PAYLOAD=$(echo -n '{"iss":"'$CLIENT_ID'","sub":"'$CLIENT_ID'","aud":"'$TOKEN_ENDPOINT'","jti":"'$JTI'","iat":'$NOW',"exp":'$EXP'}' \
  | openssl base64 -e -A | tr '+/' '-_' | tr -d '=')

# --- Sign ---
SIGNATURE=$(echo -n "$HEADER.$PAYLOAD" \
  | openssl dgst -sha256 -sign "$PRIVATE_KEY" \
  | openssl base64 -e -A | tr '+/' '-_' | tr -d '=')

ASSERTION="$HEADER.$PAYLOAD.$SIGNATURE"

# --- Get token ---
TOKEN_RESPONSE=$(curl -s -X POST "$TOKEN_ENDPOINT" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials" \
  -d "client_id=$CLIENT_ID" \
  -d "client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer" \
  -d "client_assertion=$ASSERTION" \
  -d "scope=bookings:read")

ACCESS_TOKEN=$(echo "$TOKEN_RESPONSE" | jq -r '.access_token')

if [ "$ACCESS_TOKEN" = "null" ]; then
  echo "Error:" >&2
  echo "$TOKEN_RESPONSE" | jq . >&2
  exit 1
fi

echo "Token type: $(echo "$TOKEN_RESPONSE" | jq -r '.token_type')"
echo "Expires in: $(echo "$TOKEN_RESPONSE" | jq -r '.expires_in')s"
echo "Scope: $(echo "$TOKEN_RESPONSE" | jq -r '.scope')"

# --- Call API ---
echo ""
echo "=== Bookings ==="
curl -s "http://api.petanque.life/bookings/" \
  -H "Authorization: Bearer $ACCESS_TOKEN" | jq '.total'

UI Authentication

UI applications (admin, portal, end-user app) use interactive authentication via OTP (email/SMS), OAuth2 (Google, Microsoft), or BankID. This is not relevant for M2M integrations. For details on UI auth flows, see the framework specification.

/partners

A partner represents a management company (forvaltare) that manages multiple tenants (property owners). Partners are system-level entities — they are not scoped to any tenant. Users with scope='partner' can access data across all tenants linked to their partner.

GET
/partners/
List (paginated, filterable)
POST
/partners/
Create
GET
/partners/{id}
Get by ID
PATCH
/partners/{id}
Partial update (requires If-Match)
PUT
/partners/{id}
Full replace (requires If-Match)
DELETE
/partners/{id}
Delete (requires If-Match)

Fields

NameTypeFlagsDescription
revision_idstring = null
created_atdatetime
updated_atdatetime
tenant_idObjectId = nullreadonly
is_deletedboolean = False
deleted_atdatetime = null
is_depersonalizedboolean = False
depersonalized_atdatetime = null
external_idstring = null
tagsObjectId
namestringrequired Display name of the partner organization
slugstringrequired URL-safe unique identifier for the partner
descriptionstring = nullBrief description of the partner organization
contact_emailstring = nullPrimary contact email for the partner
contact_phonestring = nullPrimary contact phone for the partner
is_enabledboolean = TrueWhether this partner is active in the system
logo_urlstring = nullURL to the partner's logo image
primary_colorstring = nullPrimary brand color in hex format (e.g. #2563eb)

Relationships

FieldReferencesOn Delete
tenant_id/tenantsnone

Create Example

POST /partners/
Content-Type: application/json
Authorization: Bearer eyJ...

{
  "name": "...",
  "slug": "..."
}

/tenants

A tenant / customer / organization using the system.

GET
/tenants/
List (paginated, filterable)
POST
/tenants/
Create
GET
/tenants/{id}
Get by ID
PATCH
/tenants/{id}
Partial update (requires If-Match)
PUT
/tenants/{id}
Full replace (requires If-Match)
DELETE
/tenants/{id}
Delete (requires If-Match)

Fields

NameTypeFlagsDescription
revision_idstring = null
created_atdatetime
updated_atdatetime
tenant_idObjectId = nullreadonly
is_deletedboolean = False
deleted_atdatetime = null
is_depersonalizedboolean = False
depersonalized_atdatetime = null
external_idstring = null
tagsObjectId
namestringrequired
slugstringrequired
is_enabledboolean = True
brandingstring
enabled_featuresarray
default_currencystringISO 4217 currency code
enabled_currenciesarrayList of ISO 4217 codes this tenant accepts
partner_idObjectId = nullFK Partner (management company) managing this tenant
partner_namestring = nullreadonly Cached name of the partner organization
parent_tenant_idObjectId = nullFK Parent tenant ID. Null = root tenant (no parent).
tenant_pathstringMaterialized path — comma-separated ancestor tenant IDs from root. Example: 'rootId,childId' for a grandchild.
depthinteger = 0Depth in the hierarchy. 0 = root tenant.
sublease_negative_amount_strategystring = rejectStrategy for negative amounts in sublease calculations: 'reject' (raise error), 'clamp_zero' (set to 0 and log), 'warn' (allow but log warning).
ai_monthly_budget_usddecimal = nullMonthly AI report budget in USD. Null = no limit.
ai_reports_enabledboolean = TrueWhether AI reports are enabled for this tenant.
max_usersinteger = null
max_storage_mbinteger = null

Relationships

FieldReferencesOn Delete
tenant_id/tenantsnone
partner_id/partnersnull
parent_tenant_id/parent-tenantsdeny

Auto-Updated Fields

These readonly fields are kept in sync automatically when the source changes.

FieldSource
partner_namepartner_id.name

Create Example

POST /tenants/
Content-Type: application/json
Authorization: Bearer eyJ...

{
  "name": "...",
  "slug": "..."
}

/users

User in the system. Simplified — no fixed organization hierarchy. Projects define their own organizational structure using Tags and custom models with CascadeConfig.

GET
/users/
List (paginated, filterable)
POST
/users/
Create
GET
/users/{id}
Get by ID
PATCH
/users/{id}
Partial update (requires If-Match)
PUT
/users/{id}
Full replace (requires If-Match)
DELETE
/users/{id}
Delete (requires If-Match)

Fields

NameTypeFlagsDescription
revision_idstring = null
created_atdatetime
updated_atdatetime
tenant_idObjectId = nullreadonly
is_deletedboolean = False
deleted_atdatetime = null
is_depersonalizedboolean = False
depersonalized_atdatetime = null
external_idstring = null
tagsObjectId
namestringrequired
emailstring = null
phonestring = null
descriptionstring = null
personal_idstring = null
rolestring = null
email_oauthstring = null
oauth_providerstring = null
scopestring = tenantAccess scope: 'system' (all data), 'partner' (partner tenants), 'tenant' (single tenant)
partner_idObjectId = nullFK Partner ID — required when scope='partner', ignored otherwise
org_scope_idObjectId = nullFK OrgNode ID — user sees data for this node and descendants only
is_enabledboolean = True
system_userboolean = False
public_userboolean = False

Relationships

FieldReferencesOn Delete
tenant_id/tenantsnone
partner_id/partnersnone
org_scope_id/org-scopesnone

Create Example

POST /users/
Content-Type: application/json
Authorization: Bearer eyJ...

{
  "name": "..."
}

/roles

A named collection of capabilities. Can inherit from other roles. System roles (is_system=True) are seeded in code and cannot be modified via API.

GET
/roles/
List (paginated, filterable)
POST
/roles/
Create
GET
/roles/{id}
Get by ID
PATCH
/roles/{id}
Partial update (requires If-Match)
PUT
/roles/{id}
Full replace (requires If-Match)
DELETE
/roles/{id}
Delete (requires If-Match)

Fields

NameTypeFlagsDescription
revision_idstring = null
created_atdatetime
updated_atdatetime
tenant_idObjectId = null
is_deletedboolean = False
deleted_atdatetime = null
is_depersonalizedboolean = False
depersonalized_atdatetime = null
external_idstring = null
tagsObjectId
namestringrequired
display_namestringrequired
descriptionstring = null
capabilitiesarray
inherits_fromarray
is_systemboolean = False

Relationships

FieldReferencesOn Delete
tenant_id/tenantsnone

Create Example

POST /roles/
Content-Type: application/json
Authorization: Bearer eyJ...

{
  "name": "...",
  "display_name": "..."
}

/role-assignments

Assigns a role to a user within a scope and a time period. A user can have multiple RoleAssignments — they accumulate into an effective capability set.

GET
/role-assignments/
List (paginated, filterable)
POST
/role-assignments/
Create
GET
/role-assignments/{id}
Get by ID
PATCH
/role-assignments/{id}
Partial update (requires If-Match)
PUT
/role-assignments/{id}
Full replace (requires If-Match)
DELETE
/role-assignments/{id}
Delete (requires If-Match)

Fields

NameTypeFlagsDescription
revision_idstring = null
created_atdatetime
updated_atdatetime
tenant_idObjectId = nullreadonly
is_deletedboolean = False
deleted_atdatetime = null
is_depersonalizedboolean = False
depersonalized_atdatetime = null
external_idstring = null
tagsObjectId
user_idObjectIdrequired FK
role_namestringrequired
scope_typestring = tenant
scope_idObjectId = nullFK
valid_fromdatetime
valid_untildatetime = null
assigned_byObjectId = null
reasonstring = null

Relationships

FieldReferencesOn Delete
tenant_id/tenantsnone
user_id/usersnone
scope_id/scopesnone

Create Example

POST /role-assignments/
Content-Type: application/json
Authorization: Bearer eyJ...

{
  "user_id": "64a1b2c3d4e5f6789012abcd",
  "role_name": "..."
}

/imports/templates

Defines how to parse and map an import file to API resources. Templates describe the source file format, field mappings, and target resource configuration. They are reusable across multiple import runs.

GET
/imports/templates/
List (paginated, filterable)
POST
/imports/templates/
Create
GET
/imports/templates/{id}
Get by ID
PATCH
/imports/templates/{id}
Partial update (requires If-Match)
PUT
/imports/templates/{id}
Full replace (requires If-Match)
DELETE
/imports/templates/{id}
Delete (requires If-Match)

Fields

NameTypeFlagsDescription
revision_idstring = null
created_atdatetime
updated_atdatetime
tenant_idObjectId = nullreadonly
is_deletedboolean = False
deleted_atdatetime = null
is_depersonalizedboolean = False
depersonalized_atdatetime = null
external_idstring = null
tagsObjectId
namestringrequired Template name, e.g. 'Bank payments CSV'
descriptionstring = nullHuman-readable description of this template
source_idObjectId = nullFK Reference to the ImportSource this template belongs to (1 source → N templates)
file_matcherstringCriteria for matching incoming files to this template
priorityinteger = 0Match priority — higher wins when multiple templates match the same file
is_activeboolean = TrueInactive templates are skipped by the matcher engine
file_typestringrequired Source file type: 'csv', 'excel', 'json', 'xml', 'fixed_width'
formatstring = csvParser format key used by ParserRegistry: 'csv', 'json', 'xml', 'flat_file'. Supersedes file_type for the streaming parser layer.
parser_configobjectFormat-specific parser options passed to the parser constructor. CSV: {delimiter, quote_char, has_header}. JSON: {records_path, mode}. XML: {record_tag, namespaces}. Flat-file: {post_types, strict_post_types}.
delimiterstring = ,Column delimiter for CSV files
encodingstring = utf-8File encoding
has_headerboolean = TrueWhether the file has a header row
skip_rowsinteger = 0Number of rows to skip at the beginning
sheet_namestring = nullSheet name for Excel files
column_widthsintegerColumn widths for fixed-width files
field_mappingarrayList of field mappings from source to target (legacy)
mappingarrayOrdered list of mapper configurations. Each entry: {"type": "", "config": {...}}. Types: field_mapping, jq, xslt, jinja2, plugin. Mappers execute in order; output of one feeds the next.
target_resourcestringrequired Target API resource, e.g. 'products', 'customers'
import_modestring = createImport mode: 'create', 'upsert', 'update', 'sync', or 'append'
upsert_keystring = nullField to match on for upsert/update/sync mode
default_valuesobjectFixed values applied to every row after field_mapping. E.g. {'category_id': 'abc', 'status': 'active'}
scope_filterobjectFilter for sync mode: which existing records to compare against. E.g. {'category_id': 'abc'}
delete_columnstring = nullColumn name in the file that indicates deletion in upsert mode. Values 'true'/'1'/'yes'/'delete' trigger soft delete.
auto_executeboolean = FalseRun import automatically after validation (for SFTP flows)
is_portal_visibleboolean = FalseWhether this template is visible in the portal for property owners
portal_labelstring = nullSimplified name shown in the portal, e.g. 'Import products'
portal_descriptionstring = nullShort description for portal users
required_columnsarrayColumns that must be present in the source file
validationstringFive-layer validation engine config: schema (JSON Schema), row_rules (regex/IBAN/BIC/orgnr/personnr/date_range/one_of/luhn), cross_row_rules (aggregates + header checks), business_rules (read-only API lookups), plugins (entry-point callables).
localestring = svDefault locale for validation messages ('sv', 'en', etc.)
cryptostringPre-flight crypto configuration: decompression, PGP/GPG decryption, and signature verification applied before parsing.
pre_transformstring = nullPython expression for row-level pre-transform
shared_from_tenant_idObjectId = nullFK If set, this template is owned by a parent tenant and shared (read-only) to its sub-tenants. Sub-tenants may use the template but not modify it.
post_process_policystring = keepWhat happens with the source file after a run: 'keep' (leave in place, system tracks via ImportRun.source_name), 'move' (move successful to completed_folder, failed to error_folder).
completed_folderstring = nullRelative folder under SFTP source where successful files are moved (requires post_process_policy='move').
error_folderstring = nullRelative folder where files are moved on total failure (parse, schema, all rows rejected). Requires post_process_policy='move'.
ignored_foldersarrayRelative folders that the source skips entirely when polling. Files in these folders never trigger an import.
temp_file_extensionsarrayFile extensions indicating an ongoing upload — the runner waits for the rename.
assert_emptyboolean = FalseIf True: this file is expected to be empty. If it contains rows a warning run is created and operators are notified. Used for reconciliation exception files.
transactionstringTransactional execution config: mode (best_effort/all_or_nothing/batched), batch_size, max_transaction_rows, stop_on_error.
idempotencystringIdempotency key config: natural_key_fields for deterministic per-row duplicate detection across re-runs.
retention_policy_idObjectId = nullFK Reference to a RetentionPolicy. When None, the tenant default policy is used.
retention_categorystring = RetentionCategory.FINANCIALRetention category — determines the default retention period when no explicit policy is set
offload_policystringControls when files are offloaded to craft-easy-jobs for async processing. Files exceeding inline_max_size_mb or inline_max_estimated_rows are queued.
schedule_expectationstring = nullExpected delivery schedule with cron syntax and tolerance window. When set, the SLA monitor job checks for missed deliveries.

Relationships

FieldReferencesOn Delete
tenant_id/tenantsnone
source_id/sourcesnone
shared_from_tenant_id/shared-from-tenantsnone
retention_policy_id/retention-policysnone

Create Example

POST /imports/templates/
Content-Type: application/json
Authorization: Bearer eyJ...

{
  "name": "...",
  "file_type": "standard",
  "target_resource": "..."
}

/imports/sources

A file source (SFTP server, watched directory, cloud bucket, email inbox, HTTP endpoint) that can feed files to one or more ImportTemplates. The relationship is 1:many — templates reference their source via ``ImportTemplate.source_id``.

GET
/imports/sources/
List (paginated, filterable)
POST
/imports/sources/
Create
GET
/imports/sources/{id}
Get by ID
PATCH
/imports/sources/{id}
Partial update (requires If-Match)
PUT
/imports/sources/{id}
Full replace (requires If-Match)
DELETE
/imports/sources/{id}
Delete (requires If-Match)

Fields

NameTypeFlagsDescription
revision_idstring = null
created_atdatetime
updated_atdatetime
tenant_idObjectId = nullreadonly
is_deletedboolean = False
deleted_atdatetime = null
is_depersonalizedboolean = False
depersonalized_atdatetime = null
external_idstring = null
tagsObjectId
namestringrequired Human-readable source name, e.g. 'Bank A SFTP inbox'
source_typestringrequired Source type: 'sftp', 'watched_directory', 's3', 'azure_blob', 'gcs', 'email_inbox', 'http_pull'
connection_configobjectType-specific connection parameters (host, path, credentials ref, etc.). Secrets must be referenced via credential_name, never stored inline.
is_activeboolean = TrueWhether this source is polled
polling_interval_secondsinteger = 300Seconds between poll cycles (default 5 minutes)
event_drivenboolean = FalseIf True, subscribe_events() is preferred over polling. Polling runs as fallback if the event stream fails.
last_successful_poll_atdatetime = nullTimestamp of the last successful poll or event batch
last_errorstring = nullLast error message from polling or health check (cleared on success)
move_unmatched_to_errorboolean = TrueMove files without a template match to the error folder

Relationships

FieldReferencesOn Delete
tenant_id/tenantsnone

Create Example

POST /imports/sources/
Content-Type: application/json
Authorization: Bearer eyJ...

{
  "name": "...",
  "source_type": "standard"
}

/rejected-records

A record rejected during import — stored for review and correction.

GET
/rejected-records/
List (paginated, filterable)
POST
/rejected-records/
Create
GET
/rejected-records/{id}
Get by ID
PATCH
/rejected-records/{id}
Partial update (requires If-Match)
PUT
/rejected-records/{id}
Full replace (requires If-Match)
DELETE
/rejected-records/{id}
Delete (requires If-Match)

Fields

NameTypeFlagsDescription
revision_idstring = null
created_atdatetime
updated_atdatetime
tenant_idObjectId = nullreadonly
is_deletedboolean = False
deleted_atdatetime = null
is_depersonalizedboolean = False
depersonalized_atdatetime = null
external_idstring = null
tagsObjectId
import_run_idObjectIdrequired FK Parent import run
template_idObjectIdrequired FK Import template used
row_numberintegerrequired Row number in the source file
original_dataobjectOriginal row data as parsed
validation_errorsarrayList of validation errors (field + message)
statusstring = pendingStatus: 'pending', 'corrected', 'retried', 'discarded'
corrected_dataobject = nullManually corrected data for retry

Relationships

FieldReferencesOn Delete
tenant_id/tenantsnone
import_run_id/import-runsnone
template_id/templatesnone

Create Example

POST /rejected-records/
Content-Type: application/json
Authorization: Bearer eyJ...

{
  "import_run_id": "64a1b2c3d4e5f6789012abcd",
  "template_id": "64a1b2c3d4e5f6789012abcd",
  "row_number": "1"
}

/alert-channels

A named alert destination (log, email, Slack, Teams, PagerDuty, webhook). Resolved by name from ``ScheduleExpectation.alert_channels`` or matched via ``AlertRoutingRule``. Config is a free-form dict whose schema depends on the channel type. Rate limiting and quiet hours prevent alert storms and out-of-hours noise.

GET
/alert-channels/
List (paginated, filterable)
POST
/alert-channels/
Create
GET
/alert-channels/{id}
Get by ID
PATCH
/alert-channels/{id}
Partial update (requires If-Match)
PUT
/alert-channels/{id}
Full replace (requires If-Match)
DELETE
/alert-channels/{id}
Delete (requires If-Match)

Fields

NameTypeFlagsDescription
revision_idstring = null
created_atdatetime
updated_atdatetime
tenant_idObjectId = nullreadonly
is_deletedboolean = False
deleted_atdatetime = null
is_depersonalizedboolean = False
depersonalized_atdatetime = null
external_idstring = null
tagsObjectId
namestringrequired Channel name, e.g. 'ops-slack', 'finance-teams'
typestringrequired Dispatch type
configobjectType-specific config (webhook_url, email, credential_name, ...)
is_activeboolean = TrueWhether this channel accepts alerts
max_alerts_per_5mininteger = 10Maximum alerts dispatched to this channel per 5-minute window
quiet_hours_startstring = nullStart of quiet period in HH:MM format (e.g. '22:00')
quiet_hours_endstring = nullEnd of quiet period in HH:MM format (e.g. '07:00')
quiet_hours_severity_overridestring = criticalDuring quiet hours, only alerts at or above this severity are sent

Relationships

FieldReferencesOn Delete
tenant_id/tenantsnone

Create Example

POST /alert-channels/
Content-Type: application/json
Authorization: Bearer eyJ...

{
  "name": "...",
  "type": "standard"
}

/alert-routing-rules

Declarative routing rule that maps alert events to channels. When an alert event is dispatched, all active rules for the tenant are evaluated in priority order. A rule matches if the event's type is in ``event_types``, its severity is in ``severity``, and all ``tags`` key-value pairs match the event's tags. All matched rules' ``channel_ids`` are collected (deduplicated) and alerts are dispatched to each resolved channel.

GET
/alert-routing-rules/
List (paginated, filterable)
POST
/alert-routing-rules/
Create
GET
/alert-routing-rules/{id}
Get by ID
PATCH
/alert-routing-rules/{id}
Partial update (requires If-Match)
PUT
/alert-routing-rules/{id}
Full replace (requires If-Match)
DELETE
/alert-routing-rules/{id}
Delete (requires If-Match)

Fields

NameTypeFlagsDescription
revision_idstring = null
created_atdatetime
updated_atdatetime
tenant_idObjectId = nullreadonly
is_deletedboolean = False
deleted_atdatetime = null
is_depersonalizedboolean = False
depersonalized_atdatetime = null
external_idstring = null
tagsobjectTag key-value pairs that must all match the event's tags
namestringrequired Rule name for display, e.g. 'finance-critical-to-pagerduty'
priorityinteger = 0Higher priority rules are evaluated first
event_typesarrayrequired Event types this rule matches, e.g. ['sla_violation', 'parse_error']
severityarrayrequired Severity levels this rule matches, e.g. ['warning', 'critical']
channel_idsarrayrequired AlertChannel IDs to dispatch to when this rule matches
is_activeboolean = True

Relationships

FieldReferencesOn Delete
tenant_id/tenantsnone

Create Example

POST /alert-routing-rules/
Content-Type: application/json
Authorization: Bearer eyJ...

{
  "name": "...",
  "event_types": "standard",
  "severity": "...",
  "channel_ids": "..."
}

/tenant-configs

Per-tenant (federation or standalone club) configuration. Each tenant configures its own OrgNode hierarchy depth, terminology, currency, season dates, languages, and federation metadata.

GET
/tenant-configs/
List (paginated, filterable)
POST
/tenant-configs/
Create
GET
/tenant-configs/{id}
Get by ID
PATCH
/tenant-configs/{id}
Partial update (requires If-Match)
PUT
/tenant-configs/{id}
Full replace (requires If-Match)
DELETE
/tenant-configs/{id}
Delete (requires If-Match)

Fields

NameTypeFlagsDescription
revision_idstring = null
created_atdatetime
updated_atdatetime
tenant_idObjectId = nullreadonly
is_deletedboolean = False
deleted_atdatetime = null
is_depersonalizedboolean = False
depersonalized_atdatetime = null
external_idstring = null
tagsObjectId
tenant_typestring = nullTenant classification (PL-T001). None for legacy tenants that have not been backfilled yet — treated as FEDERATION.
migration_sourcestring = nullRecords how this tenant was created or where it was migrated from (PL-T001). Standalone clubs get {'created_as': 'standalone_club'} at creation.
archivedboolean = FalseWhether this tenant is archived (PL-T001, PL-T004). Set to true after a standalone club is migrated or an event tenant's series has concluded. Archived tenants are read-only for audit purposes.
is_read_onlyboolean = FalseWhether this tenant is in read-only mode (PL-T010). Set to true when a club tenant has been migrated into a federation tenant. Data remains accessible for audit but no mutations are allowed.
lifecycle_statestring = activeSubscription lifecycle state (PL-T031). 'active' = normal operation; 'read_only' = subscription ended, data-export window open; 'archived' = export window ended, pending GDPR deletion; 'deleted' = data purged (GDPR retention period elapsed).
lifecycle_reasonstring = nullWhy the tenant entered a non-active lifecycle_state (PL-T047). Allows banner and email templates to distinguish voluntary cancellation from involuntary suspension due to overdue payments.
read_only_sincedatetime = nullUTC timestamp when the tenant entered read_only state (PL-T031). Set by the subscription_read_only_transition_job.
subscription_archived_atdatetime = nullUTC timestamp when the tenant was archived after the read-only export window ended (PL-T031). Distinct from 'archived' (migration).
is_demoboolean = FalseWhether this tenant is a demo/sales-demo tenant (PL-T038). When true, all public views display a 'DEMO — simulated data' banner. Demo tenants are never indexed by search engines and live exclusively in the staging environment.
demo_banner_textstring = nullCustom demo banner text (PL-T038). If None, the default 'DEMO — simulerad data / simulated data' is shown.
migration_consentstring = nullConsent record for club-to-federation migration (PL-T010). Populated when the club admin accepts a migration invitation.
event_tenant_lifespanstring = nullLifecycle configuration for event tenants (PL-T004). Only populated when tenant_type == EVENT.
event_tenant_invoice_configstring = nullBilling / invoice configuration for event tenants (PL-T004). Only populated when tenant_type == EVENT.
linked_venue_tenant_idsarrayCross-reference links to venue tenants (PL-T004). Read-only consultation links — the venue does not own event data.
linked_club_or_federation_tenant_idsarrayCross-reference links to club/federation host tenants (PL-T004). Read-only consultation links — the host does not own event data.
org_hierarchy_depthinteger = 1
org_node_typesarray
autonomous_substructureboolean = FalseWhen True, top-level districts are created with full autonomy flags (financial, discipline, ranking, pricing) enabled by default during onboarding (PL-T006). Used by federations like Spain where autonomous communities operate near-independently.
default_currencystring = EUR
season_start_monthinteger = 1
season_start_dayinteger = 1
terminologyobject
supported_languagesarray
default_languagestring = en
country_codestringrequired ISO 3166-1 alpha-2
federation_typestring = national'national', 'continental', or 'world'
federation_namestringHuman-readable federation name, e.g. 'Svenska Bouleförbundet'
federation_statusstring = active
season_typestring = calendar_yearDefault season type for this federation: 'calendar_year' (Jan–Dec), 'split_year' (e.g. Sep–Aug), or 'custom'. Used when auto-generating seasons (F03.01.15).
grace_period_daysinteger = 0Default grace period (days after season end) for late license renewals (F03.01.11). Applied to new seasons when auto-generated.
license_number_formatstring = nullFormat string for license numbers. Placeholders: {country_code}, {year}, {seq}. Example: '{country_code}-{year}-{seq:06d}'. Defaults to '{country_code}-{year}-{seq:06d}' if not set.
umpire_grade_configstringConfigurable umpire grade system for this federation
transfer_configstringConfigurable transfer rules for intra-national club transfers
insurance_configstringConfigurable insurance requirements for this federation
medical_certificate_configstringConfigurable medical certificate requirements for this federation
unauthorized_player_penalty_configstringConfigurable penalties for unauthorized player violations (F03.01.20)
international_license_configstringConfigurable international license issuance settings (F03.01.18)
license_rule_configstringPer-national license rules: issuer, approval chain, system toggle (F03.06)
sanction_configstringPer-tenant sanction configuration: deadlines, quotas, fees, blackouts (F04.02.20-24)
temporary_license_configstring = nullPer-tenant temporary license / day pass configuration (F04.03.10)
disciplinary_configstring = nullDisciplinary structure and investigation process per nation (F05.07)
federation_export_configsarrayPer-tenant federation result export format configurations (F04.06.06). First entry is the default format.
league_configstringPer-tenant league/series default configuration (F04.08)
award_voting_configstring = nullPer-tenant award voting configuration (F04.14.09)
ranking_algorithm_configstring = nullPer-tenant ranking algorithm configuration (F16.03.07)
locale_formatting_configstringPer-tenant locale-aware formatting for dates, numbers, currency (F16.03.14)
branding_configstringPer-tenant visual branding: logo, colours, fonts (F16.03.16)
game_variant_configsarrayPer-tenant game variant configurations (F16.03.18). Each entry enables and configures a game variant (pétanque, jeu provençal, sport-boules) for this federation.
branch_hierarchy_overridesarrayPer-branch OrgNode hierarchy depth overrides (F16.03.19). For nations with non-uniform hierarchy depth (e.g. Spain: Canary Islands have extra insular level).
fipjp_passport_sync_enabledboolean = TrueWhen true, new license issuances in this tenant trigger automatic passport creation/linking in the FIPJP tenant (PL-T009).
fipjp_passport_sync_endpointstring = nullBase URL of the FIPJP tenant's passport API for M2M sync calls. Example: 'https://api.petanque.life/fipjp'. None = sync disabled regardless of fipjp_passport_sync_enabled.
fipjp_m2m_credentials_secret_refstring = nullReference to the secret (Azure Key Vault or env var) that holds the M2M client credentials for FIPJP passport sync (PL-T009).
fipjp_referee_publish_enabledboolean = TrueWhen true, umpire certifications in this tenant are published to the FIPJP Global Referee Registry via the daily sync job (PL-T015).
onboarding_completed_atdatetime = nullUTC timestamp when the tenant admin completed the first-run wizard (PL-T057). When set, the wizard disappears from admin navigation.
onboarding_steps_completeddatetimePer-step completion timestamps for the admin first-run wizard (PL-T057). Keys: 'tenant_basics', 'org_hierarchy', 'seasons', 'license_types', 'stripe_connect', 'import_data'. Allows resuming the wizard from the last incomplete step.
stripe_connect_account_idstring = nullStripe Connect Express account ID for this tenant (PL-T057). Set by the account.updated webhook after Connect onboarding completes.
tournament_refund_policystring = nullRefund policy applied when a player cancels a tournament entry (PL-T063). full_until_deadline — full refund until registration_closes_at, none after. partial_before_event_week — 50 % refund up to 7 days before event start, none thereafter. no_refund — no refund ever. Null = per-type default: 'full_until_deadline' for club tenants, 'partial_before_event_week' for federation tenants.
oauth_providersarrayPer-tenant OAuth provider overrides (PL-T206). Empty list means fall back to the platform default — every gateway-configured provider is visible in every application. When non-empty, only the providers listed here are surfaced, scoped to the listed applications. Used by federations that want a curated set of providers per surface (e.g. FFPJP showing only Microsoft on admin-login because operators all use Office 365).

Relationships

FieldReferencesOn Delete
tenant_id/tenantsnone

Create Example

POST /tenant-configs/
Content-Type: application/json
Authorization: Bearer eyJ...

{
  "country_code": "..."
}

/seasons

Represents a competition season within a tenant (federation). Each tenant has exactly one active season at any time. Seasons are typically aligned with the federation's configured season_start_month/day, but dates can be customised per season. F03.01.15 — Season type tracks how the federation configures its seasons (calendar year, split year, or custom). F03.01.11 — Grace period days allows late renewals after season end.

GET
/seasons/
List (paginated, filterable)
POST
/seasons/
Create
GET
/seasons/{id}
Get by ID
PATCH
/seasons/{id}
Partial update (requires If-Match)
PUT
/seasons/{id}
Full replace (requires If-Match)
DELETE
/seasons/{id}
Delete (requires If-Match)

Fields

NameTypeFlagsDescription
revision_idstring = null
created_atdatetime
updated_atdatetime
tenant_idObjectId = nullreadonly
is_deletedboolean = False
deleted_atdatetime = null
is_depersonalizedboolean = False
depersonalized_atdatetime = null
external_idstring = null
tagsObjectId
yearintegerrequired Primary year, e.g. 2025
start_datedatetimerequired Season start date
end_datedatetimerequired Season end date
statusstring = upcoming'upcoming', 'active', or 'closed'
season_typestring = calendar_yearHow this season's boundaries are defined: calendar_year, split_year, or custom
license_renewal_open_datedatetime = nullDate when license renewal opens for this season
registration_deadlinedatetime = nullLast date to register for this season
grace_period_daysinteger = 0Number of days after season end during which late renewals are still accepted (F03.01.11). 0 = no grace period.

Relationships

FieldReferencesOn Delete
tenant_id/tenantsnone

Create Example

POST /seasons/
Content-Type: application/json
Authorization: Bearer eyJ...

{
  "year": "1",
  "start_date": "2026-04-01T08:00:00Z",
  "end_date": "2026-04-01T08:00:00Z"
}

/i18n/bundles

A set of translations for a specific language and namespace. Stores tenant-specific translation overrides (e.g. federation terminology) and community-contributed translations for additional languages.

GET
/i18n/bundles/
List (paginated, filterable)
POST
/i18n/bundles/
Create
GET
/i18n/bundles/{id}
Get by ID
PATCH
/i18n/bundles/{id}
Partial update (requires If-Match)
PUT
/i18n/bundles/{id}
Full replace (requires If-Match)
DELETE
/i18n/bundles/{id}
Delete (requires If-Match)

Fields

NameTypeFlagsDescription
revision_idstring = null
created_atdatetime
updated_atdatetime
tenant_idObjectId = nullreadonly
is_deletedboolean = False
deleted_atdatetime = null
is_depersonalizedboolean = False
depersonalized_atdatetime = null
external_idstring = null
tagsObjectId
languagestringrequired ISO 639-1 language code, e.g. 'sv', 'fr'
namespacestring = uiTranslation namespace: 'ui', 'content', 'email', etc.
translationsobjectFlat key-value translations, e.g. {'common.loading': 'Laddar...'}
is_completeboolean = FalseWhether all reference keys are translated
completeness_percentinteger = 0Percentage of reference keys that are translated
contributorstring = nullWho contributed these translations

Relationships

FieldReferencesOn Delete
tenant_id/tenantsnone

Create Example

POST /i18n/bundles/
Content-Type: application/json
Authorization: Bearer eyJ...

{
  "language": "..."
}