> ## 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.

# Habits and goals

> Manage standard habit templates, client habits, habit entries, and public-safe goal summaries through the FITsociety Public API.

Habit and goal endpoints expose a narrow public write surface. Standard habit
templates can be created, listed, updated, applied to clients, and archived.
Applied client habits can be archived, and habit entries can be created,
updated, and archived. Direct mutation of applied client habit reminder
settings, workout goals, nutrition plans, medical goals, and raw objective
answer payloads remains internal.

## Scopes

| Scope | Allows |
| :- | :- |
| `habits:read` | Read standard habit templates, client habits, and habit entries. |
| `habits:write` | Create/update/archive standard habit templates, apply templates to clients, archive client habits, and create/update/archive habit entries. |
| `goals:read` | Read public-safe client goal summaries. |
| `goals:write` | Update public-safe goal fields. |

## List standard habit templates

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

Required scope: `habits:read`

Response fields:

| Field | Type | Nullable | Description |
| :- | :- | :- | :- |
| `data.templates[].templateId` | string | no | Standard habit template ID. |
| `data.templates[].icon` | string | no | Icon key. |
| `data.templates[].color` | string | no | Display color. |
| `data.templates[].defaultLanguage` | string | yes | Declared source language, or `null` for legacy templates. |
| `data.templates[].name` | object | no | Template names by language code. |
| `data.templates[].description` | object | no | Descriptions by language code, or `{}`. |
| `data.templates[].habitType` | string | no | `build` or `quit`. |
| `data.templates[].goalPeriod` | string | no | `day`, `week`, `month`, or empty. |
| `data.templates[].logMode` | string | no | `value`. |
| `data.templates[].goalValue` | number | yes | Required for `build` templates. |
| `data.templates[].allowableValue` | number | yes | Required for `quit` templates. |
| `data.templates[].unit` | string | no | Unit key. |
| `data.templates[].customUnitName` | object | no | Unit labels by language code when `unit` is `custom`; otherwise `{}`. |
| `data.templates[].taskDaysMode` | string | no | `everyday`, `weekdays`, `weekends`, or `specific`. |
| `data.templates[].specificDaysOfWeek[]` | string\[] | no | Weekdays when `taskDaysMode` is `specific`. |
| `data.templates[].reminders[]` | array | no | Public reminder defaults with `enabled`, `time`, and a localized `message` map. |
| `data.templates[].createdAt` | string | yes | Creation timestamp. |
| `data.templates[].updatedAt` | string | yes | Update timestamp. |

Not exposed: coach IDs, source habit IDs, deletion metadata, or reminder
subdocument IDs.

## Create standard habit template

```http theme={null}
POST /public/v1/habit-templates
Authorization: Bearer <access_token>
Content-Type: application/json
```

Required scope: `habits:write`

Request body:

| Field | Type | Required | Rule |
| :- | :- | :- | :- |
| `defaultLanguage` | string | yes | BCP-47 source language, for example `nl`. |
| `name` | object | yes | Complete language map; max 80 characters per translation. |
| `description` | object | no | Complete language map; max 500 characters per translation, or `{}`. |
| `habitType` | string | yes | `build` or `quit`. |
| `goalPeriod` | string | no | Defaults to `day`; allowed: `day`, `week`, `month`, or empty. |
| `logMode` | string | no | Defaults to `value`; templates currently support `value` only. |
| `goalValue` | number/string | conditional | Required and positive for `build` templates. |
| `allowableValue` | number/string | conditional | Required and non-negative for `quit` templates; zero is allowed. |
| `unit` | string | yes | Unit key. |
| `customUnitName` | object | conditional | Complete language map, max 80 characters per translation. Required for `custom`; omit or send `{}` for other units. |
| `taskDaysMode` | string | no | Defaults to `everyday`. |
| `specificDaysOfWeek[]` | string\[] | conditional | Required when `taskDaysMode` is `specific`. |
| `reminders[].time` | string | no | `HH:mm` in the client's timezone when applied. |
| `reminders[].enabled` | boolean | no | Defaults to `true`. |
| `reminders[].message` | object | no | Complete language map; max 200 characters per translation, or `{}`. |

Unknown fields are rejected with `PUBLIC_API_UNKNOWN_FIELDS`. A company cannot
have two active templates with the same source-language `name` and `habitType`.

Text fields follow the [localized text contract](/public-api/localization):
up to 25 language codes, an explicit default on create/first legacy update, and
complete replacement for supplied maps. Every nonempty map must contain the
default language. The `reminders` array replaces all reminders when supplied;
include each reminder's complete message map. Applied client habits and reminder
delivery use the source text; this does not add language selection to client apps.

Habit templates store translations directly in their text fields, with a single
`defaultLanguage`. The existing internal API adapts those fields to source
strings for the current editor. An unchanged source string preserves its
translations; changing that string clears translations for that field. Existing
legacy strings remain readable without a bulk migration.

```bash cURL theme={null}
curl -X POST "https://api.fitsociety.io/public/v1/habit-templates" \
  -H "Authorization: Bearer $FITSOCIETY_ACCESS_TOKEN" \
  -H "Idempotency-Key: habit-template-create-20260826" \
  -H "Content-Type: application/json" \
  -d '{
    "defaultLanguage": "en",
    "name": { "en": "Drink water", "nl": "Water drinken" },
    "description": { "en": "Daily hydration target" },
    "habitType": "build",
    "goalPeriod": "day",
    "logMode": "value",
    "goalValue": 2.5,
    "unit": "custom",
    "customUnitName": { "en": "liter", "nl": "liter" },
    "taskDaysMode": "everyday",
    "reminders": [{ "enabled": true, "time": "08:30", "message": { "en": "Drink water", "nl": "Water drinken" } }]
  }'
```

## Update standard habit template

```http theme={null}
PATCH /public/v1/habit-templates/{templateId}
Authorization: Bearer <access_token>
Content-Type: application/json
```

Required scope: `habits:write`

This updates the standard template only. Client habits that were already created
from the template are not changed.

Request body: any field from create may be sent as a partial update. The body
must contain at least one field. Unknown fields are rejected with
`PUBLIC_API_UNKNOWN_FIELDS`.

```bash cURL theme={null}
curl -X PATCH "https://api.fitsociety.io/public/v1/habit-templates/64b64c0f2f5f4c00123456a1" \
  -H "Authorization: Bearer $FITSOCIETY_ACCESS_TOKEN" \
  -H "Idempotency-Key: habit-template-update-20260826" \
  -H "Content-Type: application/json" \
  -d '{
    "name": { "en": "Drink 2.5 liters of water", "nl": "Drink 2,5 liter water" },
    "goalValue": 2.5
  }'
```

Response fields: same `data.template` contract as create/list standard habit
templates.

## Apply standard habit templates

```http theme={null}
POST /public/v1/clients/{clientId}/habits/apply-templates
Authorization: Bearer <access_token>
Content-Type: application/json
```

Required scope: `habits:write`

This creates active client habits from standard habit templates. The response
returns the created `habitId` values; use those IDs when logging habit entries.

Request body:

| Field | Type | Required | Rule |
| :- | :- | :- | :- |
| `templateIds[]` | string\[] | yes | One to 25 active template IDs from the same company. |
| `startDate` | date/string | no | Defaults to today in UTC. |
| `endDate` | date/string | no | Optional end date; must be on or after `startDate`. |
| `duplicateStrategy` | string | no | `skip` by default, or `fail`. |

```bash cURL theme={null}
curl -X POST "https://api.fitsociety.io/public/v1/clients/64b64c0f2f5f4c0012345678/habits/apply-templates" \
  -H "Authorization: Bearer $FITSOCIETY_ACCESS_TOKEN" \
  -H "Idempotency-Key: habit-template-apply-20260826" \
  -H "Content-Type: application/json" \
  -d '{
    "templateIds": ["64b64c0f2f5f4c00123456a1"],
    "startDate": "2026-08-26",
    "duplicateStrategy": "skip"
  }'
```

Response fields:

| Field | Type | Nullable | Description |
| :- | :- | :- | :- |
| `data.application.clientId` | string | no | Client ID. |
| `data.application.duplicateStrategy` | string | no | Applied duplicate strategy. |
| `data.application.totalSelected` | integer | no | Number of selected templates. |
| `data.application.created` | integer | no | Number of client habits created. |
| `data.application.skipped` | integer | no | Number of duplicate habits skipped. |
| `data.application.results[].templateId` | string | no | Template ID. |
| `data.application.results[].habitId` | string | no | Created or existing habit ID. |
| `data.application.results[].status` | string | no | `created` or `skipped`. |
| `data.application.results[].reason` | string | no | Skip reason when applicable. |

## Archive standard habit template

```http theme={null}
PATCH /public/v1/habit-templates/{templateId}/archive
Authorization: Bearer <access_token>
```

Required scope: `habits:write`

Archive is a soft delete for the standard template. Already applied client
habits are not deleted.

Response fields:

| Field | Type | Nullable | Description |
| :- | :- | :- | :- |
| `data.template.templateId` | string | no | Template ID. |
| `data.template.archived` | boolean | no | Always `true` for successful responses. |

## List client habits

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

Required scope: `habits:read`

Response fields:

| Field | Type | Nullable | Description |
| :- | :- | :- | :- |
| `data.habits[].habitId` | string | no | Habit ID. |
| `data.habits[].name` | string | no | Habit name. |
| `data.habits[].description` | string | no | Habit description. |
| `data.habits[].habitType` | string | no | Habit type. |
| `data.habits[].goalPeriod` | string | no | Goal period. |
| `data.habits[].logMode` | string | no | Log mode. |
| `data.habits[].goalValue` | number | yes | Goal value. |
| `data.habits[].allowableValue` | number | yes | Allowance/tolerance. |
| `data.habits[].unit` | string | no | Unit. |
| `data.habits[].color` | string | no | Display color. |

Not exposed: reminder settings, notification copy, actor IDs, deleted flags,
audit fields, or scheduling internals.

## List habit entries

```http theme={null}
GET /public/v1/clients/{clientId}/habits/entries?dateFrom=2026-07-01T00:00:00.000Z&dateTo=2026-07-31T23:59:59.999Z
Authorization: Bearer <access_token>
```

Required scope: `habits:read`

Validation:

| Parameter | Type | Required | Rule |
| :- | :- | :- | :- |
| `dateFrom` | ISO date-time | yes | Valid date-time. |
| `dateTo` | ISO date-time | yes | Valid date-time, must be after `dateFrom`. |
| `habitId` | string | no | Valid habit ObjectId belonging to the client/company. |

Range limit: maximum 92 days.

Response fields:

| Field | Type | Nullable | Description |
| :- | :- | :- | :- |
| `data.truncated` | boolean | no | Whether the internal max entries limit was reached. |
| `data.entries[].entryId` | string | no | Entry ID. |
| `data.entries[].habitId` | string | yes | Habit ID. |
| `data.entries[].occurredAt` | string | yes | Occurrence timestamp. |
| `data.entries[].localDate` | string | no | Canonical local date. |
| `data.entries[].periodKey` | string | no | Period bucket key. |
| `data.entries[].value` | number | yes | Logged value. |
| `data.entries[].unit` | string | no | Unit at log time. |
| `data.entries[].note` | string | no | Entry note. |
| `data.periodSummary[].habitId` | string | yes | Habit ID. |
| `data.periodSummary[].periodKey` | string | no | Period key. |
| `data.periodSummary[].entryCount` | integer | no | Entries in that period. |
| `data.periodSummary[].totalValue` | number | no | Total logged value. |

Not exposed: source, actor IDs, reminder metadata, audit fields, or deleted
entries.

<CodeGroup>
  ```bash cURL theme={null}
  curl -H "Authorization: Bearer $FITSOCIETY_ACCESS_TOKEN" \
    "https://api.fitsociety.io/public/v1/clients/64b64c0f2f5f4c0012345678/habits/entries?dateFrom=2026-07-01T00:00:00.000Z&dateTo=2026-07-31T23:59:59.999Z"
  ```

  ```javascript JavaScript theme={null}
  const clientId = "64b64c0f2f5f4c0012345678";
  const query = new URLSearchParams({
    dateFrom: "2026-07-01T00:00:00.000Z",
    dateTo: "2026-07-31T23:59:59.999Z",
  });

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

  const body = await response.json();
  console.log(body.data.entries, body.data.periodSummary);
  ```

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

  client_id = "64b64c0f2f5f4c0012345678"
  response = requests.get(
      f"https://api.fitsociety.io/public/v1/clients/{client_id}/habits/entries",
      headers={"Authorization": f"Bearer {os.environ['FITSOCIETY_ACCESS_TOKEN']}"},
      params={
          "dateFrom": "2026-07-01T00:00:00.000Z",
          "dateTo": "2026-07-31T23:59:59.999Z",
      },
  )
  response.raise_for_status()

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

Response:

```json theme={null}
{
  "data": {
    "truncated": false,
    "entries": [
      {
        "entryId": "64b64c0f2f5f4c00123456a0",
        "habitId": "64b64c0f2f5f4c00123456a1",
        "occurredAt": "2026-07-14T18:00:00.000Z",
        "localDate": "2026-07-14",
        "periodKey": "2026-W29",
        "value": 2500,
        "unit": "ml",
        "note": "Water intake"
      }
    ],
    "periodSummary": [
      {
        "habitId": "64b64c0f2f5f4c00123456a1",
        "periodKey": "2026-W29",
        "entryCount": 1,
        "totalValue": 2500
      }
    ]
  },
  "meta": {
    "requestId": "req_0123456789abcdef",
    "rateLimit": { "limit": 10, "remaining": 9, "resetSeconds": 1 }
  }
}
```

## Create habit entry

```http theme={null}
POST /public/v1/clients/{clientId}/habits/{habitId}/entries
Authorization: Bearer <access_token>
Content-Type: application/json
```

Required scope: `habits:write`

Request body:

| Field | Type | Required | Rule |
| :- | :- | :- | :- |
| `occurredAt` | UTC ISO date-time | yes | Must match `YYYY-MM-DDTHH:mm:ss(.SSS)Z`. |
| `value` | number/string | conditional | Required for value-based habits. Localized numeric strings such as `"82,5"` are accepted. Must be non-negative. |
| `note` | string | no | Maximum 1000 characters. |

Validation:

| Rule | Behavior |
| :- | :- |
| Client access | Client must belong to the authenticated company. |
| Habit access | Habit must belong to the client and company and must not be deleted. |
| Date window | `occurredAt` must be within habit `startDate` and `endDate` when configured. |
| Specific weekdays | Habits configured for specific weekdays can only be logged on those days in the client's timezone. |
| Max value | `value` cannot exceed a positive `allowableValue`. A zero quit goal allows logging positive values to record missed goals. |
| Unknown fields | Rejected with `PUBLIC_API_UNKNOWN_FIELDS`. |

Create is idempotent per `clientId`, `habitId`, and calculated `localDate`.
When an entry already exists for that local day, it is replaced instead of
creating a duplicate.

Response fields:

| Field | Type | Nullable | Description |
| :- | :- | :- | :- |
| `data.entry.entryId` | string | no | Habit entry ID. |
| `data.entry.habitId` | string | yes | Habit ID. |
| `data.entry.occurredAt` | string | yes | Occurrence timestamp. |
| `data.entry.localDate` | string | no | Canonical local date in the client's timezone. |
| `data.entry.periodKey` | string | no | Period bucket key. |
| `data.entry.value` | number | yes | Logged value. |
| `data.entry.unit` | string | no | Unit at log time. |
| `data.entry.note` | string | no | Entry note. |
| `data.created` | boolean | no | `true` when a new entry was created. |
| `data.replaced` | boolean | no | `true` when an existing local-day entry was replaced. |

## Update habit entry

```http theme={null}
PATCH /public/v1/clients/{clientId}/habits/entries/{entryId}
Authorization: Bearer <access_token>
Content-Type: application/json
```

Required scope: `habits:write`

Allowed fields: `occurredAt`, `value`, and `note`. At least one field is
required. The same date, weekday, note length, and numeric value validations as
Create habit entry apply.

Response fields:

| Field | Type | Nullable | Description |
| :- | :- | :- | :- |
| `data.entry.entryId` | string | no | Habit entry ID. |
| `data.entry.habitId` | string | yes | Habit ID. |
| `data.entry.occurredAt` | string | yes | Occurrence timestamp. |
| `data.entry.localDate` | string | no | Canonical local date in the client's timezone. |
| `data.entry.periodKey` | string | no | Period bucket key. |
| `data.entry.value` | number | yes | Logged value. |
| `data.entry.unit` | string | no | Unit at log time. |
| `data.entry.note` | string | no | Entry note. |

## Archive client habit

```http theme={null}
PATCH /public/v1/clients/{clientId}/habits/{habitId}/archive
Authorization: Bearer <access_token>
```

Required scope: `habits:write`

Archive is a soft delete for the client habit itself. The habit is excluded from
Public API habit reads and reminder notifications are cleaned up. Existing habit
entries are not hard-deleted.

Response fields:

| Field | Type | Nullable | Description |
| :- | :- | :- | :- |
| `data.habit.habitId` | string | no | Habit ID. |
| `data.habit.archived` | boolean | no | Always `true` for successful responses. |
| `data.habit.updatedAt` | string | yes | Update timestamp. |

## Archive habit entry

```http theme={null}
PATCH /public/v1/clients/{clientId}/habits/entries/{entryId}/archive
Authorization: Bearer <access_token>
```

Required scope: `habits:write`

Archive is a soft delete. The entry is excluded from Public API read endpoints
after archiving.

Response fields:

| Field | Type | Nullable | Description |
| :- | :- | :- | :- |
| `data.entry.entryId` | string | no | Habit entry ID. |
| `data.entry.archived` | boolean | no | Always `true` for successful responses. |
| `data.entry.updatedAt` | string | yes | Update timestamp. |

## Get client goals

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

Required scope: `goals:read`

Measurement goals include only active, non-deleted settings with a valid type
available to the authenticated company. Custom goals owned by another company
are excluded, including when the client belongs to both companies. System
measurement goals remain shared client settings. The same filtering applies to
the goals returned after an update.

Response fields:

| Field | Type | Nullable | Description |
| :- | :- | :- | :- |
| `data.goals.physicalActivityGoal` | string | no | Public physical activity goal text. |
| `data.goals.currentWeightKg` | number | yes | Current weight goal value. |
| `data.goals.targetWeightKg` | number | yes | Target weight goal value. |
| `data.goals.timeline.startDate` | string | yes | Goal start date. |
| `data.goals.timeline.endDate` | string | yes | Goal end date. |
| `data.goals.dailyGoals.waterIntakeMl` | number | yes | Water intake goal. |
| `data.goals.dailyGoals.dailySteps` | number | yes | Daily step goal. |
| `data.goals.dailyGoals.sleepDurationHours` | number | yes | Sleep duration goal. |
| `data.goals.nutritionGoals.daily.caloriesKcal` | number | yes | Standard daily calorie target. |
| `data.goals.nutritionGoals.daily.macroNutrients.carbohydratesPercent` | number | yes | Standard daily carbohydrate percentage target. |
| `data.goals.nutritionGoals.daily.macroNutrients.carbohydratesGram` | number | yes | Standard daily carbohydrate gram target. |
| `data.goals.nutritionGoals.daily.macroNutrients.proteinPercent` | number | yes | Standard daily protein percentage target. |
| `data.goals.nutritionGoals.daily.macroNutrients.proteinGram` | number | yes | Standard daily protein gram target. |
| `data.goals.nutritionGoals.daily.macroNutrients.fatPercent` | number | yes | Standard daily fat percentage target. |
| `data.goals.nutritionGoals.daily.macroNutrients.fatGram` | number | yes | Standard daily fat gram target. |
| `data.goals.nutritionGoals.weekly.enabled` | boolean | no | Whether weekday-specific nutrition targets are active. |
| `data.goals.nutritionGoals.weekly.{weekday}.caloriesKcal` | number | yes | Weekday-specific calorie target for `monday` through `sunday`. |
| `data.goals.nutritionGoals.weekly.{weekday}.macroNutrients.*` | number | yes | Weekday-specific macro target fields using the same macro keys as `daily`. |
| `data.goals.measurementGoals[].typeId` | string | yes | Measurement type ID. |
| `data.goals.measurementGoals[].typeName` | string | no | Measurement type name. |
| `data.goals.measurementGoals[].goal` | number | yes | Goal value. |
| `data.goals.measurementGoals[].unit` | string | no | Goal unit. |

Not exposed: nutrition plans, food logs, meal-plan content, medical details,
injuries, medications, allergies, raw ObjectiveQuestion documents, objective
answer payloads, or internal notes.

## Update client goals

```http theme={null}
PATCH /public/v1/clients/{clientId}/goals
Authorization: Bearer <access_token>
Content-Type: application/json
```

Required scope: `goals:write`

Only these public fields are accepted:

| Field | Type | Required | Rule |
| :- | :- | :- | :- |
| `physicalActivityGoal` | string | no | `default`, `Lose Weight`, `Maintain Weight`, or `Gain Weight`. |
| `currentWeightKg` | number/string | no | Non-negative number. Localized numeric strings are accepted. |
| `targetWeightKg` | number/string | no | Non-negative number. |
| `timeline.startDate` | date/null | no | `YYYY-MM-DD` or `null`. |
| `timeline.endDate` | date/null | no | `YYYY-MM-DD` or `null`. |
| `dailyGoals.waterIntakeMl` | number/string | no | Non-negative number. |
| `dailyGoals.dailySteps` | number/string | no | Non-negative number. |
| `dailyGoals.sleepDurationHours` | number/string | no | Non-negative number. |
| `nutritionGoals.daily.caloriesKcal` | number/string | no | Non-negative standard daily calorie target. |
| `nutritionGoals.daily.macroNutrients.carbohydratesPercent` | number/string | no | Non-negative standard daily carbohydrate percentage target. |
| `nutritionGoals.daily.macroNutrients.carbohydratesGram` | number/string | no | Non-negative standard daily carbohydrate gram target. |
| `nutritionGoals.daily.macroNutrients.proteinPercent` | number/string | no | Non-negative standard daily protein percentage target. |
| `nutritionGoals.daily.macroNutrients.proteinGram` | number/string | no | Non-negative standard daily protein gram target. |
| `nutritionGoals.daily.macroNutrients.fatPercent` | number/string | no | Non-negative standard daily fat percentage target. |
| `nutritionGoals.daily.macroNutrients.fatGram` | number/string | no | Non-negative standard daily fat gram target. |
| `nutritionGoals.weekly.enabled` | boolean/string | no | `true` or `false`. String booleans are accepted for compatibility. |
| `nutritionGoals.weekly.{weekday}.caloriesKcal` | number/string | no | Non-negative weekday calorie target for `monday` through `sunday`. |
| `nutritionGoals.weekly.{weekday}.macroNutrients.*` | number/string | no | Non-negative weekday macro targets using the same macro keys as `daily`. |

Unknown fields are rejected with `PUBLIC_API_UNKNOWN_GOAL_FIELDS`. Empty patches
return `NOTHING_TO_UPDATE`. Nested objects are flattened before validation, so
callers may send either nested JSON or dotted field paths when supported by
their client.

Response fields:

| Field | Type | Nullable | Description |
| :- | :- | :- | :- |
| `data.goals.physicalActivityGoal` | string | no | Public physical activity goal text. |
| `data.goals.currentWeightKg` | number | yes | Current weight goal value. |
| `data.goals.targetWeightKg` | number | yes | Target weight goal value. |
| `data.goals.timeline.startDate` | string | yes | Goal start date. |
| `data.goals.timeline.endDate` | string | yes | Goal end date. |
| `data.goals.dailyGoals.waterIntakeMl` | number | yes | Water intake goal. |
| `data.goals.dailyGoals.dailySteps` | number | yes | Daily step goal. |
| `data.goals.dailyGoals.sleepDurationHours` | number | yes | Sleep duration goal. |
| `data.goals.nutritionGoals.daily.caloriesKcal` | number | yes | Standard daily calorie target. |
| `data.goals.nutritionGoals.daily.macroNutrients.*` | number | yes | Standard daily macro target fields. |
| `data.goals.nutritionGoals.weekly.enabled` | boolean | no | Whether weekday-specific nutrition targets are active. |
| `data.goals.nutritionGoals.weekly.{weekday}.caloriesKcal` | number | yes | Weekday-specific calorie target. |
| `data.goals.nutritionGoals.weekly.{weekday}.macroNutrients.*` | number | yes | Weekday-specific macro target fields. |
| `data.goals.measurementGoals[].typeId` | string | yes | Measurement type ID. |
| `data.goals.measurementGoals[].typeName` | string | no | Measurement type name. |
| `data.goals.measurementGoals[].goal` | number | yes | Goal value. |
| `data.goals.measurementGoals[].unit` | string | no | Goal unit. |
| `data.updatedFields[]` | string\[] | no | Internal field paths updated by the patch. |
| `data.updatedAt` | string | yes | Objective document update timestamp. |

Goal writes deliberately exclude nutrition plans, food logs, meal-plan content,
workout-plan targets, medical fields, injuries, medications, allergies, and raw
objective answer payloads.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X PATCH "https://api.fitsociety.io/public/v1/clients/64b64c0f2f5f4c0012345678/goals" \
    -H "Authorization: Bearer $FITSOCIETY_ACCESS_TOKEN" \
    -H "Idempotency-Key: goals-update-jane-20260823" \
    -H "Content-Type: application/json" \
    -d '{
      "physicalActivityGoal": "Lose Weight",
      "targetWeightKg": 78,
      "timeline": { "startDate": "2026-08-01", "endDate": "2026-12-01" },
      "dailyGoals": { "dailySteps": 10000, "waterIntakeMl": 2500 },
      "nutritionGoals": {
        "daily": {
          "caloriesKcal": 2200,
          "macroNutrients": {
            "proteinGram": 160,
            "carbohydratesGram": 250,
            "fatGram": 70
          }
        },
        "weekly": {
          "enabled": true,
          "monday": {
            "caloriesKcal": 2400,
            "macroNutrients": {
              "proteinGram": 170,
              "carbohydratesGram": 280,
              "fatGram": 75
            }
          }
        }
      }
    }'
  ```

  ```javascript JavaScript theme={null}
  const clientId = "64b64c0f2f5f4c0012345678";
  const response = await fetch(
    `https://api.fitsociety.io/public/v1/clients/${clientId}/goals`,
    {
      method: "PATCH",
      headers: {
        Authorization: `Bearer ${process.env.FITSOCIETY_ACCESS_TOKEN}`,
        "Idempotency-Key": "goals-update-jane-20260823",
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        physicalActivityGoal: "Lose Weight",
        targetWeightKg: 78,
        timeline: { startDate: "2026-08-01", endDate: "2026-12-01" },
        dailyGoals: { dailySteps: 10000, waterIntakeMl: 2500 },
        nutritionGoals: {
          daily: {
            caloriesKcal: 2200,
            macroNutrients: {
              proteinGram: 160,
              carbohydratesGram: 250,
              fatGram: 70,
            },
          },
          weekly: {
            enabled: true,
            monday: {
              caloriesKcal: 2400,
              macroNutrients: {
                proteinGram: 170,
                carbohydratesGram: 280,
                fatGram: 75,
              },
            },
          },
        },
      }),
    },
  );

  const body = await response.json();
  console.log(body.data.updatedFields, body.data.goals);
  ```

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

  client_id = "64b64c0f2f5f4c0012345678"
  response = requests.patch(
      f"https://api.fitsociety.io/public/v1/clients/{client_id}/goals",
      headers={
          "Authorization": f"Bearer {os.environ['FITSOCIETY_ACCESS_TOKEN']}",
          "Idempotency-Key": "goals-update-jane-20260823",
      },
      json={
          "physicalActivityGoal": "Lose Weight",
          "targetWeightKg": 78,
          "timeline": {"startDate": "2026-08-01", "endDate": "2026-12-01"},
          "dailyGoals": {"dailySteps": 10000, "waterIntakeMl": 2500},
          "nutritionGoals": {
              "daily": {
                  "caloriesKcal": 2200,
                  "macroNutrients": {
                      "proteinGram": 160,
                      "carbohydratesGram": 250,
                      "fatGram": 70,
                  },
              },
              "weekly": {
                  "enabled": True,
                  "monday": {
                      "caloriesKcal": 2400,
                      "macroNutrients": {
                          "proteinGram": 170,
                          "carbohydratesGram": 280,
                          "fatGram": 75,
                      },
                  },
              },
          },
      },
  )
  response.raise_for_status()

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

Response excerpt:

```json theme={null}
{
  "data": {
    "goals": {
      "physicalActivityGoal": "Lose Weight",
      "currentWeightKg": 82.5,
      "targetWeightKg": 78,
      "timeline": { "startDate": "2026-08-01", "endDate": "2026-12-01" },
      "dailyGoals": {
        "waterIntakeMl": 2500,
        "dailySteps": 10000,
        "sleepDurationHours": 8
      },
      "nutritionGoals": {
        "daily": {
          "caloriesKcal": 2200,
          "macroNutrients": {
            "carbohydratesPercent": 45,
            "carbohydratesGram": 250,
            "proteinPercent": 29,
            "proteinGram": 160,
            "fatPercent": 29,
            "fatGram": 70
          }
        },
        "weekly": {
          "enabled": true,
          "monday": {
            "caloriesKcal": 2400,
            "macroNutrients": {
              "carbohydratesPercent": null,
              "carbohydratesGram": 280,
              "proteinPercent": null,
              "proteinGram": 170,
              "fatPercent": null,
              "fatGram": 75
            }
          }
        }
      },
      "measurementGoals": []
    },
    "updatedFields": [
      "goals.physicalActivity",
      "weight.targetWeightKg",
      "timeline.startDate",
      "timeline.endDate",
      "dailyGoals.dailySteps",
      "dailyGoals.waterIntake",
      "macroNutrients.protienGram",
      "macroNutrients.carbohydratesGram",
      "macroNutrients.fatGram",
      "standard.caloriesStandardKcal",
      "weeklyNutritionTargetsEnabled",
      "weeklyNutritionTargets.monday.caloriesKcal",
      "weeklyNutritionTargets.monday.macroNutrients.proteinGram",
      "weeklyNutritionTargets.monday.macroNutrients.carbohydratesGram",
      "weeklyNutritionTargets.monday.macroNutrients.fatGram"
    ],
    "updatedAt": "2026-08-23T10:00:00.000Z"
  },
  "meta": {
    "requestId": "req_0123456789abcdef",
    "rateLimit": { "limit": 10, "remaining": 8, "resetSeconds": 1 }
  }
}
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.