Skip to main content
This contract is for company environments explicitly configured for externally managed app access. Existing manually managed members keep their current access policy. An external payment is not a FITsociety membership invoice.

Company opt-in and scopes

A company administrator or authorized staff member creates a Public API OAuth client through POST /app/v1/integrations/public-api/clients, using their coach token and the integrations.api_credentials.create permission. The company must have Public API provider access. The company is derived from that authenticated context; do not pass a company ID. This example enables client creation and app/AI grant synchronization:
externalAppAccessEnabled defaults to false. Set it at OAuth client creation; client_app_access:write must be among that client’s configured scopes when the option is true. Public API callers cannot enable it themselves. Add only the workout scopes and explicit health consent required by your programme operations; see authentication and consent. Store the returned clientId and one-time clientSecret for the OAuth token exchange. To rotate the secret later, use the returned _id with POST /app/v1/integrations/public-api/clients/{id}/rotate-secret through the company management API. Secret rotation preserves reference and grant ownership; creating a replacement OAuth client creates a separate namespace. For a new company-client relationship, the required external app-access policy commits atomically with the relationship before any creation or linking email runs. The same rule applies when linking an existing global account to a new company relationship. Existing manual relationships are never silently taken over. Existing creation/linking emails may arrive before an active grant has been synchronized. An invitation alone does not provide app access; respect the existing email behavior and do not send a duplicate invitation. Announce paid activation only after the effective-access read confirms the requested rights.
  • client_access:read: read owned grants and current effective access.
  • client_app_access:write: manage the app capability.
  • client_ai_access:write: manage the client_ai capability.
  • Client and workout operations retain their existing scopes and consent gates.
The company must still have the client-AI licence and enabled supporting modules. An AI capability authorizes the existing permitted client-AI features; it does not add REST endpoints for AI chat, nutrition recognition or workout generation. Every actual feature keeps its own licensing and module checks.

Grant status versus client status

An external grant authorizes capabilities for one company-client relationship. Its status is separate from the client’s relationship, membership and portal statuses. Writing a grant does not activate, pause, archive or otherwise change those lifecycle fields. See Client statuses and app access for their values and supported mutations. There is no stored expired or scheduled grant status. An active grant can be in the future or already expired. FITsociety checks its dates when evaluating access; it does not rewrite the grant status when time passes. For example, relationship.status: "active", portal status enabled and grant status active can still produce effective.app: false after validUntil in an externally managed relationship. A company block or portal restriction can also prevent access during a grant’s valid interval. Always confirm the current effective result instead of deriving it from one status field. A deleted or missing global client account always has effective.app: false and effective.clientAi: false. A new active revision then returns 403 access.client_unavailable. Owned grants remain readable and revocable for reconciliation; an exact stored-grant revision replay does not reactivate the account.

Create, renew or revoke a grant

Use PUT /public/v1/clients/{clientId}/access-entitlements/{externalId}. The identifier is opaque, at most 200 characters, and belongs to the authenticated API client within its company. No payment provider prefix or billing cadence is required. Every write also requires an Idempotency-Key.
Dates must be finite UTC instants ending in Z, with validFrom < validUntil. Access applies at validFrom <= now < validUntil. Expired intervals may be recorded for reconciliation; they do not grant current access. Renew by updating the same identifier with a newer positive safe-integer revision and a new paid interval. Revoke with status: "revoked" and a newer revision. Retained records prevent an older active message from restoring access. A relationship supports at most 100 retained entitlement records; renew the same source entitlement rather than creating a new record for each payment period. Permissions are checked for every existing and requested capability, including removal and revocation. A caller with only app-write scope cannot remove an AI capability from an existing combined grant. A new active app+AI write is rejected entirely with 403 access.ai_unavailable when AI is unavailable; the previous grant is unchanged. Revocation remains possible without an AI licence. Same revision and normalized content confirms the existing grant. An older revision returns 409 access.stale_revision; conflicting content at the same revision returns 409 access.revision_conflict. Secret rotation keeps the same ownership identity. A new OAuth client is a separate source and requires an explicit ownership migration; do not replace one to rotate its secret.

Confirm effective access

Read GET /public/v1/clients/{clientId}/access-entitlements after synchronization. The response includes only this API client’s grants and an effective summary:
The example shows a new managed relationship before its first grant. Responses use the standard { data, meta } envelope. A successful stored-grant write does not guarantee that the full product is currently accessible: company blocks, portal policy, feature settings and licence changes still apply. HTTP idempotency replays contain the original response snapshot; GET returns current state. For externally managed relationships, an administrator company block also denies login, company selection and existing-session access without modifying the grant. Expiry is evaluated on existing authenticated sessions and access checks. It needs neither a webhook nor a scheduled database update. Other valid grants and manual AI rights remain independent. Revoking one integration’s grant cannot remove another source’s access. There is no guaranteed grant-expiry webhook in this version; integrations should read current state when needed.

Recoverable client and programme synchronization

Use durable external references for client, exercise and workout-plan identity. A typical server-side flow is:
  1. Resolve the client by owned external ID; optionally find an existing client by exact email and bind its reference.
  2. Create or link a new client through the existing client API when needed.
  3. Resolve controlled exercise IDs and create the assigned programme through the existing workout API.
  4. Synchronize the paid access interval and read back effective app and AI access.
  5. Announce activation only when all requested capabilities are effective.
Persist each completed step in the integrating system. Programme metadata, days, exercises and supersets are separate existing resources; creating a plan does not implicitly import arbitrary nested content. Do not invent exercise classification data to bypass required validation. Unchanged renewals reuse the same programme and preserve training logs. Dynamic capability permissions and the current API-client opt-in are checked before an HTTP idempotency response can replay. The stored grant is checked again during each actual mutation.