Skip to main content

GOFA Client API Documentation

This document describes the available endpoints under /api/client for client integrations.


Authentication

POST /api/client/token/

Obtain a JWT token for client API access.

Request Body:

{
"clientId": "string",
"clientSecret": "string"
}

Response:

  • 200 OK: { token: "string" }
  • 400 Bad Request: { error: "string" }
  • 401 Unauthorized: { error: "string" }
  • 500 Internal Server Error: { error: "string" }

Authentication Methods

The API supports multiple authentication methods. Use any one of the following:

JWT-based authentication for client applications. Obtained from /api/client/token/.

ClientToken: <jwt_token>

Firebase ID Token

Firebase Authentication ID token for user-specific operations.

Authorization: Bearer <firebase_id_token>

Legacy Methods (Deprecated)

  • AdminToken: <admin_token> - Legacy admin authentication
  • RevokableToken: <revokable_token> - Legacy revokable token authentication

Note: Session cookies are also supported as a fallback authentication method.


Users

GET /api/client/[clientId]/users

List all users for a client, or search for a user by exact email match using the email query parameter.

Headers:

  • ClientToken: <client_token> OR
  • Authorization: Bearer <firebase_id_token> OR
  • AdminToken: <admin_token> (deprecated)

Query Parameters:

  • email (optional): If provided, returns the user with the exact matching email. If not found, returns user: null.
  • userType (optional): Filter by user type - 'player', 'cms', or 'silverCare'.
  • search (optional): Search across displayName, email, and phoneNumber fields.
  • attributeKey (optional): Search by custom attribute key (requires attributeValue).
  • attributeValue (optional): Exact value to match for the specified attributeKey.
  • sortBy (optional): Sort field - 'createdAt', 'updatedAt', 'displayName', or 'email'. Default: 'createdAt'.
  • sortDesc (optional): Sort in descending order. Default: false.
  • limit (optional): Maximum number of results. Default: 50.
  • offset (optional): Number of results to skip for pagination. Default: 0.

Note: When using attributeKey and attributeValue, the search will find users with exact matches in their attributes field. For example, ?attributeKey=className&attributeValue=yoga-101 will find users where attributes.className equals "yoga-101".

Response:

  • 200 OK:
    • { users: Array<User> } (if no email parameter)
    • { user: User } (if email parameter and user found)
    • { user: null } (if email parameter and no user found)
  • 401 Unauthorized: { error: string }
  • 400 Bad Request: { error: string }
  • 500 Internal Server Error: { error: string }

POST /api/client/[clientId]/users

Create a new user for a client. This endpoint creates a Firebase Auth user and a corresponding Firestore user document.

Headers:

  • ClientToken: <client_token> OR
  • Authorization: Bearer <firebase_id_token> OR
  • AdminToken: <admin_token> (deprecated)

Request Body:

All fields except those marked optional are required.

{
"email": "string (valid email)",
"password": "string (min 6 chars)",
"firstName": "string (min 2 chars)",
"lastName": "string",
"gender": "MALE" | "FEMALE" | "OTHER" | "UNSPECIFIED" (optional),
"birthDate": "YYYY-MM-DD" (optional),
"height": integer (optional),
"weight": integer (optional),
"activityLevel": "LOW" | "MEDIUM" | "HIGH" (optional),
"fitnessLevel": "BEGINNER" | "INTERMEDIATE" | "ADVANCED" (optional),
"attributes": {
"customKey1": "any value",
"customKey2": 123,
"customKey3": true
} (optional)
}

Response:

  • 200 OK:

    {
    "success": true,
    "customToken": "string",
    "uid": "string",
    "user": {
    /* user object */
    }
    }
  • 400 Bad Request: { errors: { field: [messages] } } (validation error)

  • 401 Unauthorized: { error: string }

  • 409 Conflict: { error: string } (user already exists)

  • 500 Internal Server Error: { error: string }

Notes:

  • Do not include userId or name in the request body. The user ID is generated by Firebase Auth.
  • Only fields listed above are accepted; unknown fields will be rejected.
  • The returned customToken can be used to sign in the user immediately after registration.

GET /api/client/[clientId]/users/[userId]

Get a specific user by ID.

Headers:

  • ClientToken: <client_token> OR
  • Authorization: Bearer <firebase_id_token> OR
  • AdminToken: <admin_token> (deprecated) OR
  • RevokableToken: <revokable_token> (deprecated)

Response:

  • 200 OK: { user: User }
  • 404 Not Found: { error: string }
  • 401 Unauthorized: { error: string }
  • 400 Bad Request: { error: string }
  • 500 Internal Server Error: { error: string }

PATCH /api/client/[clientId]/users/[userId]

Update fields for a user.

Headers:

  • ClientToken: <client_token> OR
  • Authorization: Bearer <firebase_id_token> OR
  • AdminToken: <admin_token> (deprecated) OR
  • RevokableToken: <revokable_token> (deprecated)

Request Body:

{
// ...fields to update (only fields defined in the schema are accepted)
}

Field Validation:

  • Only the following fields defined in the user schema are accepted. Attempts to patch unknown fields will result in a 400 Bad Request error with details.

Allowed fields:

  • email (string, optional)
  • displayName (string, optional)
  • photoURL (string, optional)
  • cms (object, optional)
    • status (string, required)
    • role (enum, optional)
  • player (object, required)
    • status (string, required)
  • personalInfo (object, optional)
    • firstName (string, optional)
    • lastName (string, optional)
    • gender (enum, optional) — one of "MALE", "FEMALE", "NON_BINARY", "UNDEFINED"
    • birthDate (string, optional, ISO date)
    • height (number, optional)
    • weight (number, optional)
    • activityLevel (enum, optional) — one of "NOT_ACTIVE", "LOW", "MODERATE", "HIGH"
    • fitnessLevel (enum, optional) — one of "BEGINNER", "INTERMEDIATE", "ADVANCED"
  • locale (enum, optional)
  • settings (object, optional)
    • lessons (object, optional)
      • bookmarkedLessonIds (array of strings, optional)
      • skipWarmUp (boolean, optional)
      • skipCoolDown (boolean, optional)
    • challenge (object, optional)
      • bookmarkedChallengeIds (array of strings, optional)
  • attributes (object, optional) — Custom key-value pairs for third-party integrations
    • Any keys and values (strings, numbers, booleans, arrays, objects) are allowed

Response:

  • 200 OK: { success: true }
  • 400 Bad Request: { error: string, details?: any } (invalid or unknown fields)
  • 401 Unauthorized: { error: string }
  • 500 Internal Server Error: { error: string }

DELETE /api/client/[clientId]/users/[userId]

Delete a user and optionally their associated data.

Headers:

  • ClientToken: <client_token> OR
  • Authorization: Bearer <firebase_id_token> OR
  • AdminToken: <admin_token> (deprecated) OR
  • RevokableToken: <revokable_token> (deprecated)

Request Body (Optional):

{
"deleteUserData": boolean (optional, default: true)
}

Request Body Fields:

  • deleteUserData (optional, default: true): When true, deletes all associated user data including:

    • Fall risk assessment results
    • Lesson play records

    When false, only deletes the user document itself.

Response:

  • 200 OK:

    {
    "success": true,
    "deletedUserData": boolean
    }
  • 401 Unauthorized: { error: string }

  • 400 Bad Request: { error: string }

  • 500 Internal Server Error: { error: string }

Notes:

  • The deletion is performed atomically using Firestore batch operations.
  • If no request body is provided, deleteUserData defaults to true.
  • Invalid JSON in the request body is ignored and defaults are used.

Working with Custom Attributes

The attributes field allows third-party partners to store custom data alongside user records. This field accepts any key-value pairs and is completely flexible.

Creating Users with Attributes

POST /api/client/[clientId]/users
{
"email": "user@example.com",
"password": "password123",
"firstName": "John",
"lastName": "Doe",
"attributes": {
"className": "yoga-101",
"centerName": "Downtown Wellness",
"caregiverName": "Jane Smith",
"membershipLevel": "premium",
"joinDate": "2024-01-15",
"preferences": {
"notifications": true,
"language": "en"
}
}
}

Updating Attributes

You can update the entire attributes object or merge new values:

PATCH /api/client/[clientId]/users/[userId]
{
"attributes": {
"className": "advanced-yoga",
"lastVisit": "2024-09-02",
"completedSessions": 15
}
}

Searching by Attributes

Find users by specific attribute values:

GET /api/client/[clientId]/users?attributeKey=className&attributeValue=yoga-101
GET /api/client/[clientId]/users?attributeKey=centerName&attributeValue=Downtown%20Wellness

Important Notes:

  • Attribute searches perform exact matches only
  • For complex queries involving multiple attributes, consider fetching users and filtering client-side
  • Firestore automatically creates indexes for single-field queries, but composite indexes may be needed for complex multi-field searches

JWT ClientId Validation

All endpoints that require authentication now validate that the clientId in the JWT matches the clientId in the request path. If they do not match, the API will return:

  • 401 Unauthorized: { error: "Token clientId does not match request clientId" }

This applies to:

  • GET /api/client/[clientId]/users
  • POST /api/client/[clientId]/users
  • GET /api/client/[clientId]/users/[userId]
  • PATCH /api/client/[clientId]/users/[userId]
  • DELETE /api/client/[clientId]/users/[userId]

Ensure your JWT is generated for the correct client.


Notes

  • All endpoints require a valid JWT obtained from /api/client/token/ except for the token endpoint itself.
  • All responses are JSON.
  • User object fields depend on your integration and may include custom fields.