> ## Documentation Index
> Fetch the complete documentation index at: https://docs.fitsociety.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Platform and audit resources

> Inspect the authenticated Public API client, available capabilities, scopes, and redacted audit logs.

Use platform endpoints to verify OAuth client context and to inspect which
Public API resources are available to an integration. Audit endpoints are
read-only and intentionally redact request headers, raw response bodies, token
identifiers, IP addresses, user agents, provider payloads, and secrets.

## Scopes

| Scope             | Allows                                                                         |
| :---------------- | :----------------------------------------------------------------------------- |
| `platform:read`   | Read the authenticated API client profile, capability list, and scope catalog. |
| `audit_logs:read` | Read redacted Public API audit log metadata for the authenticated company.     |

## Get API client profile

```http theme={null}
GET /public/v1/me
Authorization: Bearer <access_token>
```

Required scope: `platform:read`

Response fields:

| Field                            | Type   | Nullable | Description                         |
| :------------------------------- | :----- | :------- | :---------------------------------- |
| `data.profile.clientId`          | string | no       | Public API OAuth client ID.         |
| `data.profile.publicApiClientId` | string | no       | Public API client document ID.      |
| `data.profile.companyId`         | string | no       | Authenticated company ID.           |
| `data.profile.name`              | string | no       | Client display name.                |
| `data.profile.status`            | string | no       | `active` or `revoked`.              |
| `data.profile.scopes[]`          | array  | no       | Scopes granted to the access token. |
| `data.profile.createdAt`         | string | yes      | Client creation timestamp.          |
| `data.profile.updatedAt`         | string | yes      | Client update timestamp.            |

The response never includes client secrets, secret hashes, token hashes,
`tokenId`, last-used IP, or raw auth headers.

The examples below also show how to read `meta.rateLimit` from the response
envelope to throttle an integration before it hits `429` responses.

<CodeGroup>
  ```bash cURL theme={null}
  curl -H "Authorization: Bearer $FITSOCIETY_ACCESS_TOKEN" \
    "https://api.fitsociety.io/public/v1/me"
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch("https://api.fitsociety.io/public/v1/me", {
    headers: {
      Authorization: `Bearer ${process.env.FITSOCIETY_ACCESS_TOKEN}`,
    },
  });

  const body = await response.json();
  console.log(body.data.profile.name, body.data.profile.scopes);

  // Every resource response carries rate-limit state in meta.rateLimit.
  const rateLimit = body.meta.rateLimit;
  if (rateLimit && rateLimit.remaining === 0) {
    const waitSeconds = rateLimit.retryAfterSeconds ?? rateLimit.resetSeconds;
    await new Promise((resolve) => setTimeout(resolve, waitSeconds * 1000));
  }
  ```

  ```python Python theme={null}
  import os
  import time
  import requests

  response = requests.get(
      "https://api.fitsociety.io/public/v1/me",
      headers={"Authorization": f"Bearer {os.environ['FITSOCIETY_ACCESS_TOKEN']}"},
  )
  response.raise_for_status()

  body = response.json()
  print(body["data"]["profile"]["name"], body["data"]["profile"]["scopes"])

  # Every resource response carries rate-limit state in meta.rateLimit.
  rate_limit = body["meta"].get("rateLimit")
  if rate_limit and rate_limit["remaining"] == 0:
      time.sleep(rate_limit.get("retryAfterSeconds", rate_limit["resetSeconds"]))
  ```
</CodeGroup>

Response:

```json theme={null}
{
  "data": {
    "profile": {
      "clientId": "fs_client_0123456789abcdef",
      "publicApiClientId": "64b64c0f2f5f4c00123456d5",
      "companyId": "64b64c0f2f5f4c00123456d6",
      "name": "CRM integration",
      "status": "active",
      "scopes": ["clients:read", "clients:write", "platform:read"],
      "createdAt": "2026-05-01T09:00:00.000Z",
      "updatedAt": "2026-08-01T09:00:00.000Z"
    }
  },
  "meta": {
    "requestId": "req_0123456789abcdef",
    "rateLimit": { "limit": 10, "remaining": 9, "resetSeconds": 1 }
  }
}
```

## Health

```http theme={null}
GET /public/v1/health
Authorization: Bearer <access_token>
```

Required scope: `platform:read`

This endpoint is a lightweight authenticated health check for integrations. It
uses Public API bearer authentication and the same company-bound token context,
but it intentionally returns a compact health payload instead of a full resource
DTO.

Response fields:

| Field     | Type   | Nullable | Description                                                   |
| :-------- | :----- | :------- | :------------------------------------------------------------ |
| `status`  | string | no       | `ok` when the API process accepted the authenticated request. |
| `company` | string | yes      | Company ID derived from the bearer token.                     |

The request never accepts `companyId` from the query string or body.

## List capabilities

```http theme={null}
GET /public/v1/capabilities
Authorization: Bearer <access_token>
```

Required scope: `platform:read`

Response fields:

| Field                                        | Type   | Nullable               | Description                                                                                                               |
| :------------------------------------------- | :----- | :--------------------- | :------------------------------------------------------------------------------------------------------------------------ |
| `data.capabilities.scopes[].value`           | string | no                     | Available Public API scope value.                                                                                         |
| `data.capabilities.scopes[].requiresConsent` | string | omitted when not gated | Present for [consent-gated scopes](/public-api/authentication#consent-gated-scopes): `health` or `private_communication`. |
| `data.capabilities.endpoints[].method`       | string | no                     | HTTP method.                                                                                                              |
| `data.capabilities.endpoints[].path`         | string | no                     | Public API path template.                                                                                                 |
| `data.capabilities.endpoints[].scope`        | string | no                     | Scope required by the endpoint.                                                                                           |

Nutrition and workout endpoints are intentionally not listed in this v1
capability catalog.

## List scopes

```http theme={null}
GET /public/v1/scopes
Authorization: Bearer <access_token>
```

Required scope: `platform:read`

Response fields:

| Field                           | Type   | Nullable               | Description                                                                                                               |
| :------------------------------ | :----- | :--------------------- | :------------------------------------------------------------------------------------------------------------------------ |
| `data.scopes[].value`           | string | no                     | Available Public API scope value.                                                                                         |
| `data.scopes[].requiresConsent` | string | omitted when not gated | Present for [consent-gated scopes](/public-api/authentication#consent-gated-scopes): `health` or `private_communication`. |

This endpoint returns only the scope catalog. It does not return endpoint
metadata, OAuth client secrets, token IDs, token hashes, or company internals.

## List audit logs

```http theme={null}
GET /public/v1/audit-logs?page=1&limit=100&eventType=resource.denied
Authorization: Bearer <access_token>
```

Required scope: `audit_logs:read`

Query parameters:

| Parameter   | Type    | Required | Rule                                  |
| :---------- | :------ | :------- | :------------------------------------ |
| `page`      | integer | no       | Minimum `1`.                          |
| `limit`     | integer | no       | Default `100`, maximum `100`.         |
| `eventType` | string  | no       | Filter by audit event type.           |
| `clientId`  | string  | no       | Filter by Public API OAuth client ID. |

Response fields:

| Field                                | Type    | Nullable | Description                            |
| :----------------------------------- | :------ | :------- | :------------------------------------- |
| `data.page`                          | integer | no       | Current page.                          |
| `data.limit`                         | integer | no       | Page size after cap.                   |
| `data.total`                         | integer | no       | Total matching audit logs.             |
| `data.totalPages`                    | integer | no       | Total pages.                           |
| `data.hasNextPage`                   | boolean | no       | Next page availability.                |
| `data.hasPrevPage`                   | boolean | no       | Previous page availability.            |
| `data.auditLogs[].auditLogId`        | string  | no       | Audit log ID.                          |
| `data.auditLogs[].publicApiClientId` | string  | yes      | Public API client document ID.         |
| `data.auditLogs[].clientId`          | string  | no       | Public API OAuth client ID.            |
| `data.auditLogs[].eventType`         | string  | no       | Audit event type.                      |
| `data.auditLogs[].method`            | string  | no       | HTTP method when available.            |
| `data.auditLogs[].path`              | string  | no       | Request path when available.           |
| `data.auditLogs[].statusCode`        | integer | yes      | HTTP status code when recorded.        |
| `data.auditLogs[].scopes[]`          | array   | no       | Token scopes recorded for the request. |
| `data.auditLogs[].reason`            | string  | no       | Redacted reason when present.          |
| `data.auditLogs[].createdAt`         | string  | yes      | Audit timestamp.                       |

<CodeGroup>
  ```bash cURL theme={null}
  curl -H "Authorization: Bearer $FITSOCIETY_ACCESS_TOKEN" \
    "https://api.fitsociety.io/public/v1/audit-logs?page=1&limit=100&eventType=resource.denied"
  ```

  ```javascript JavaScript theme={null}
  const query = new URLSearchParams({
    page: "1",
    limit: "100",
    eventType: "resource.denied",
  });

  const response = await fetch(
    `https://api.fitsociety.io/public/v1/audit-logs?${query}`,
    {
      headers: {
        Authorization: `Bearer ${process.env.FITSOCIETY_ACCESS_TOKEN}`,
      },
    },
  );

  const body = await response.json();
  console.log(body.data.total, body.data.auditLogs);
  ```

  ```python Python theme={null}
  import os
  import requests

  response = requests.get(
      "https://api.fitsociety.io/public/v1/audit-logs",
      headers={"Authorization": f"Bearer {os.environ['FITSOCIETY_ACCESS_TOKEN']}"},
      params={"page": 1, "limit": 100, "eventType": "resource.denied"},
  )
  response.raise_for_status()

  body = response.json()
  print(body["data"]["total"], body["data"]["auditLogs"])
  ```
</CodeGroup>

Response:

```json theme={null}
{
  "data": {
    "page": 1,
    "limit": 100,
    "total": 1,
    "totalPages": 1,
    "hasNextPage": false,
    "hasPrevPage": false,
    "auditLogs": [
      {
        "auditLogId": "64b64c0f2f5f4c00123456d7",
        "publicApiClientId": "64b64c0f2f5f4c00123456d5",
        "clientId": "fs_client_0123456789abcdef",
        "eventType": "resource.denied",
        "method": "GET",
        "path": "/public/v1/finance/invoices",
        "statusCode": 403,
        "scopes": ["clients:read"],
        "reason": "missing_scope",
        "createdAt": "2026-08-22T14:00:00.000Z"
      }
    ]
  },
  "meta": {
    "requestId": "req_0123456789abcdef",
    "rateLimit": { "limit": 10, "remaining": 8, "resetSeconds": 1 }
  }
}
```

## Get audit log

```http theme={null}
GET /public/v1/audit-logs/{auditLogId}
Authorization: Bearer <access_token>
```

Required scope: `audit_logs:read`

Response fields:

| Field                             | Type      | Nullable | Description                            |
| :-------------------------------- | :-------- | :------- | :------------------------------------- |
| `data.auditLog.auditLogId`        | string    | no       | Audit log ID.                          |
| `data.auditLog.publicApiClientId` | string    | yes      | Public API client document ID.         |
| `data.auditLog.clientId`          | string    | no       | Public API OAuth client ID.            |
| `data.auditLog.eventType`         | string    | no       | Audit event type.                      |
| `data.auditLog.method`            | string    | no       | HTTP method when available.            |
| `data.auditLog.path`              | string    | no       | Request path when available.           |
| `data.auditLog.statusCode`        | integer   | yes      | HTTP status code when recorded.        |
| `data.auditLog.scopes[]`          | string\[] | no       | Token scopes recorded for the request. |
| `data.auditLog.reason`            | string    | no       | Redacted reason when present.          |
| `data.auditLog.createdAt`         | string    | yes      | Audit timestamp.                       |

Invalid ID shape returns `400 PUBLIC_API_INVALID_AUDIT_LOG_ID`; unknown
company-scoped IDs return `404 PUBLIC_API_AUDIT_LOG_NOT_FOUND`.
