Skip to content

API and webhooks

Connect NetFit to your own tools. Every request acts as a signed-in user and sees only what that user sees in the app.

Download the OpenAPI 3.1 document

Authentication

Send the access token of a signed-in NetFit user as a bearer token in the Authorization header. The token expires: refresh it the way the app does.

Authorization: Bearer ACCESS_TOKEN

Endpoints

62 endpoints

Webhooks

  • GET/api/webhook-endpoints

    List the coach's webhook endpoints (never the secret)

    Response 200WebhookEndpointList
    {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "url": {
            "type": "string"
          },
          "events": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "active": {
            "type": "boolean"
          },
          "consecutiveFailures": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Final failed deliveries in a row since the last success"
          },
          "disabledAt": {
            "description": "When NetFit switched the endpoint off after repeated failures",
            "type": [
              "string",
              "null"
            ]
          },
          "disabledReason": {
            "anyOf": [
              {
                "type": "string",
                "enum": [
                  "consecutive_failures"
                ]
              },
              {
                "type": "null"
              }
            ]
          },
          "secretRotatedAt": {
            "type": [
              "string",
              "null"
            ]
          },
          "previousSecretExpiresAt": {
            "description": "Until when deliveries also carry a v1 signed with the previous secret",
            "type": [
              "string",
              "null"
            ]
          },
          "createdAt": {
            "type": "string"
          },
          "updatedAt": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "url",
          "events",
          "active",
          "consecutiveFailures",
          "disabledAt",
          "disabledReason",
          "secretRotatedAt",
          "previousSecretExpiresAt",
          "createdAt",
          "updatedAt"
        ],
        "additionalProperties": false
      }
    }
  • POST/api/webhook-endpoints

    Add a webhook endpoint; the signing secret is returned once

    Request bodyWebhookEndpointCreate
    {
      "type": "object",
      "properties": {
        "url": {
          "type": "string",
          "minLength": 1,
          "maxLength": 2048
        },
        "events": {
          "minItems": 1,
          "maxItems": 5,
          "type": "array",
          "items": {
            "type": "string",
            "enum": [
              "workout.completed",
              "checkin.submitted",
              "booking.created",
              "booking.cancelled",
              "client.joined"
            ]
          }
        }
      },
      "required": [
        "url",
        "events"
      ],
      "additionalProperties": false
    }
    Response 201WebhookEndpointCreated
    {
      "type": "object",
      "properties": {
        "id": {
          "type": "string"
        },
        "url": {
          "type": "string"
        },
        "events": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "active": {
          "type": "boolean"
        },
        "consecutiveFailures": {
          "type": "integer",
          "minimum": 0,
          "maximum": 9007199254740991,
          "description": "Final failed deliveries in a row since the last success"
        },
        "disabledAt": {
          "description": "When NetFit switched the endpoint off after repeated failures",
          "type": [
            "string",
            "null"
          ]
        },
        "disabledReason": {
          "anyOf": [
            {
              "type": "string",
              "enum": [
                "consecutive_failures"
              ]
            },
            {
              "type": "null"
            }
          ]
        },
        "secretRotatedAt": {
          "type": [
            "string",
            "null"
          ]
        },
        "previousSecretExpiresAt": {
          "description": "Until when deliveries also carry a v1 signed with the previous secret",
          "type": [
            "string",
            "null"
          ]
        },
        "createdAt": {
          "type": "string"
        },
        "updatedAt": {
          "type": "string"
        },
        "secret": {
          "type": "string",
          "format": "starts_with",
          "pattern": "^whsec_.*"
        }
      },
      "required": [
        "id",
        "url",
        "events",
        "active",
        "consecutiveFailures",
        "disabledAt",
        "disabledReason",
        "secretRotatedAt",
        "previousSecretExpiresAt",
        "createdAt",
        "updatedAt",
        "secret"
      ],
      "additionalProperties": false
    }
  • PATCH/api/webhook-endpoints/{id}

    Change the URL or events of an endpoint, or switch it on or off (on also clears an automatic switch-off)

    Parameters
    NameLocationRequiredDescription
    idpathYes
    Request bodyWebhookEndpointUpdate
    {
      "type": "object",
      "properties": {
        "url": {
          "type": "string",
          "minLength": 1,
          "maxLength": 2048
        },
        "events": {
          "minItems": 1,
          "maxItems": 5,
          "type": "array",
          "items": {
            "type": "string",
            "enum": [
              "workout.completed",
              "checkin.submitted",
              "booking.created",
              "booking.cancelled",
              "client.joined"
            ]
          }
        },
        "active": {
          "type": "boolean"
        }
      },
      "additionalProperties": false
    }
    Response 200WebhookEndpoint
    {
      "type": "object",
      "properties": {
        "id": {
          "type": "string"
        },
        "url": {
          "type": "string"
        },
        "events": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "active": {
          "type": "boolean"
        },
        "consecutiveFailures": {
          "type": "integer",
          "minimum": 0,
          "maximum": 9007199254740991,
          "description": "Final failed deliveries in a row since the last success"
        },
        "disabledAt": {
          "description": "When NetFit switched the endpoint off after repeated failures",
          "type": [
            "string",
            "null"
          ]
        },
        "disabledReason": {
          "anyOf": [
            {
              "type": "string",
              "enum": [
                "consecutive_failures"
              ]
            },
            {
              "type": "null"
            }
          ]
        },
        "secretRotatedAt": {
          "type": [
            "string",
            "null"
          ]
        },
        "previousSecretExpiresAt": {
          "description": "Until when deliveries also carry a v1 signed with the previous secret",
          "type": [
            "string",
            "null"
          ]
        },
        "createdAt": {
          "type": "string"
        },
        "updatedAt": {
          "type": "string"
        }
      },
      "required": [
        "id",
        "url",
        "events",
        "active",
        "consecutiveFailures",
        "disabledAt",
        "disabledReason",
        "secretRotatedAt",
        "previousSecretExpiresAt",
        "createdAt",
        "updatedAt"
      ],
      "additionalProperties": false
    }
  • DELETE/api/webhook-endpoints/{id}

    Remove an endpoint and its delivery log

    Parameters
    NameLocationRequiredDescription
    idpathYes
    Response 200WebhookDeleteResponse
    {
      "type": "object",
      "properties": {
        "deleted": {
          "type": "boolean",
          "const": true
        }
      },
      "required": [
        "deleted"
      ],
      "additionalProperties": false
    }
  • GET/api/webhook-endpoints/{id}/deliveries

    The latest 50 deliveries to an endpoint, newest first

    Parameters
    NameLocationRequiredDescription
    idpathYes
    Response 200WebhookDeliveryList
    {
      "maxItems": 50,
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "event": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "delivered",
              "failed"
            ]
          },
          "attempts": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991
          },
          "lastStatusCode": {
            "anyOf": [
              {
                "type": "integer",
                "minimum": -9007199254740991,
                "maximum": 9007199254740991
              },
              {
                "type": "null"
              }
            ]
          },
          "lastError": {
            "type": [
              "string",
              "null"
            ]
          },
          "createdAt": {
            "type": "string"
          },
          "deliveredAt": {
            "type": [
              "string",
              "null"
            ]
          },
          "nextAttemptAt": {
            "type": "string"
          },
          "replayOfId": {
            "description": "The delivery this one replays",
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "id",
          "event",
          "status",
          "attempts",
          "lastStatusCode",
          "lastError",
          "createdAt",
          "deliveredAt",
          "nextAttemptAt",
          "replayOfId"
        ],
        "additionalProperties": false
      }
    }
  • POST/api/webhook-endpoints/{id}/deliveries/{deliveryId}/replay

    Send a finished (delivered or failed) delivery again, as a copy with a new delivery id; 409 while it is still pending or the endpoint is off

    Parameters
    NameLocationRequiredDescription
    idpathYes
    deliveryIdpathYes
    Response 202WebhookReplayResponse
    {
      "type": "object",
      "properties": {
        "id": {
          "type": "string"
        },
        "event": {
          "type": "string"
        },
        "status": {
          "type": "string",
          "const": "pending"
        },
        "createdAt": {
          "type": "string"
        },
        "replayOfId": {
          "type": "string"
        }
      },
      "required": [
        "id",
        "event",
        "status",
        "createdAt",
        "replayOfId"
      ],
      "additionalProperties": false
    }
  • POST/api/webhook-endpoints/{id}/rotate-secret

    Replace the signing secret; the old one keeps signing as a second v1 for 24 hours unless revokePrevious is true

    Parameters
    NameLocationRequiredDescription
    idpathYes
    Request bodyWebhookSecretRotate
    {
      "type": "object",
      "properties": {
        "revokePrevious": {
          "type": "boolean"
        }
      },
      "additionalProperties": false
    }
    Response 200WebhookSecretRotated
    {
      "type": "object",
      "properties": {
        "id": {
          "type": "string"
        },
        "url": {
          "type": "string"
        },
        "events": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "active": {
          "type": "boolean"
        },
        "consecutiveFailures": {
          "type": "integer",
          "minimum": 0,
          "maximum": 9007199254740991,
          "description": "Final failed deliveries in a row since the last success"
        },
        "disabledAt": {
          "description": "When NetFit switched the endpoint off after repeated failures",
          "type": [
            "string",
            "null"
          ]
        },
        "disabledReason": {
          "anyOf": [
            {
              "type": "string",
              "enum": [
                "consecutive_failures"
              ]
            },
            {
              "type": "null"
            }
          ]
        },
        "secretRotatedAt": {
          "type": [
            "string",
            "null"
          ]
        },
        "previousSecretExpiresAt": {
          "description": "Until when deliveries also carry a v1 signed with the previous secret",
          "type": [
            "string",
            "null"
          ]
        },
        "createdAt": {
          "type": "string"
        },
        "updatedAt": {
          "type": "string"
        },
        "secret": {
          "type": "string",
          "format": "starts_with",
          "pattern": "^whsec_.*"
        }
      },
      "required": [
        "id",
        "url",
        "events",
        "active",
        "consecutiveFailures",
        "disabledAt",
        "disabledReason",
        "secretRotatedAt",
        "previousSecretExpiresAt",
        "createdAt",
        "updatedAt",
        "secret"
      ],
      "additionalProperties": false
    }
  • POST/api/webhook-endpoints/{id}/test

    Queue a signed `ping` delivery to an active endpoint

    Parameters
    NameLocationRequiredDescription
    idpathYes
    Response 202WebhookTestResponse
    {
      "type": "object",
      "properties": {
        "id": {
          "type": "string"
        },
        "event": {
          "type": "string",
          "const": "ping"
        },
        "status": {
          "type": "string",
          "const": "pending"
        },
        "createdAt": {
          "type": "string"
        }
      },
      "required": [
        "id",
        "event",
        "status",
        "createdAt"
      ],
      "additionalProperties": false
    }

Clients

  • GET/api/clients

    A coach's client roster, or a client's coaches

    Parameters
    NameLocationRequiredDescription
    trainerIdqueryNoThe coach's own id: list their clients
    clientIdqueryNoThe client's own id: list their coaches
    sortqueryNoWith `trainerId`: `recent` (default, newest first) or `risk` (highest at-risk score first)
    limitqueryNoRows to return (default 500, at most 2000)
    Response 200ClientListResponse
    {
      "anyOf": [
        {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "clientId": {
                "type": "string"
              },
              "trainerId": {
                "type": "string"
              },
              "status": {
                "type": "string",
                "description": "Link status; `ended` links are never listed"
              },
              "notes": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "createdAt": {
                "type": "string",
                "description": "ISO-8601 timestamp"
              },
              "updatedAt": {
                "type": "string",
                "description": "ISO-8601 timestamp"
              },
              "client": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string"
                  },
                  "name": {
                    "type": "string"
                  },
                  "email": {
                    "type": "string"
                  },
                  "avatar": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "bio": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "phone": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "dateOfBirth": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "height": {
                    "type": [
                      "number",
                      "null"
                    ]
                  },
                  "createdAt": {
                    "type": "string",
                    "description": "ISO-8601 timestamp"
                  }
                },
                "required": [
                  "id",
                  "name",
                  "email",
                  "avatar",
                  "bio",
                  "phone",
                  "dateOfBirth",
                  "height",
                  "createdAt"
                ],
                "additionalProperties": {}
              },
              "risk": {
                "description": "W4: the at-risk score; null for a link that is not active",
                "anyOf": [
                  {
                    "type": "object",
                    "properties": {
                      "score": {
                        "type": "integer",
                        "minimum": 0,
                        "maximum": 100
                      },
                      "band": {
                        "type": "string",
                        "enum": [
                          "low",
                          "medium",
                          "high"
                        ]
                      },
                      "reasons": {
                        "type": "array",
                        "items": {
                          "type": "object",
                          "properties": {
                            "key": {
                              "type": "string",
                              "description": "Locale key, `insights.risk.reasons.<reason>`"
                            },
                            "weight": {
                              "type": "integer",
                              "minimum": -9007199254740991,
                              "maximum": 9007199254740991
                            },
                            "detail": {
                              "type": "object",
                              "propertyNames": {
                                "type": "string"
                              },
                              "additionalProperties": {
                                "type": "number"
                              },
                              "description": "ICU values for the locale key"
                            }
                          },
                          "required": [
                            "key",
                            "weight",
                            "detail"
                          ],
                          "additionalProperties": false
                        }
                      }
                    },
                    "required": [
                      "score",
                      "band",
                      "reasons"
                    ],
                    "additionalProperties": false
                  },
                  {
                    "type": "null"
                  }
                ]
              }
            },
            "required": [
              "id",
              "clientId",
              "trainerId",
              "status",
              "notes",
              "createdAt",
              "updatedAt",
              "client"
            ],
            "additionalProperties": {}
          },
          "description": "`?trainerId=`: the coach roster"
        },
        {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "clientId": {
                "type": "string"
              },
              "trainerId": {
                "type": "string"
              },
              "status": {
                "type": "string",
                "description": "Link status; `ended` links are never listed"
              },
              "notes": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "createdAt": {
                "type": "string",
                "description": "ISO-8601 timestamp"
              },
              "updatedAt": {
                "type": "string",
                "description": "ISO-8601 timestamp"
              },
              "trainer": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string"
                  },
                  "name": {
                    "type": "string"
                  },
                  "email": {
                    "type": "string"
                  },
                  "avatar": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "bio": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "specializations": {
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                },
                "required": [
                  "id",
                  "name",
                  "email",
                  "avatar",
                  "bio",
                  "specializations"
                ],
                "additionalProperties": {}
              }
            },
            "required": [
              "id",
              "clientId",
              "trainerId",
              "status",
              "notes",
              "createdAt",
              "updatedAt",
              "trainer"
            ],
            "additionalProperties": {}
          },
          "description": "`?clientId=`: the client's coaches"
        }
      ]
    }
  • GET/api/clients/{id}

    One client's profile with their latest metrics, nutrition, tasks and workouts

    Parameters
    NameLocationRequiredDescription
    idpathYes
    Response 200ClientDetailResponse
    {
      "type": "object",
      "properties": {
        "id": {
          "type": "string"
        },
        "name": {
          "type": "string"
        },
        "email": {
          "type": "string"
        },
        "avatar": {
          "type": [
            "string",
            "null"
          ]
        },
        "bio": {
          "type": [
            "string",
            "null"
          ]
        },
        "phone": {
          "type": [
            "string",
            "null"
          ]
        },
        "dateOfBirth": {
          "type": [
            "string",
            "null"
          ]
        },
        "height": {
          "type": [
            "number",
            "null"
          ]
        },
        "createdAt": {
          "type": "string",
          "description": "ISO-8601 timestamp"
        },
        "metricEntries": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              }
            },
            "required": [
              "id"
            ],
            "additionalProperties": {}
          },
          "description": "Latest 10"
        },
        "nutritionEntries": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              }
            },
            "required": [
              "id"
            ],
            "additionalProperties": {}
          },
          "description": "Latest 10"
        },
        "assignedTasks": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              }
            },
            "required": [
              "id"
            ],
            "additionalProperties": {}
          },
          "description": "Latest 10"
        },
        "assignedWorkoutsClient": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "workout": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string"
                  }
                },
                "required": [
                  "id"
                ],
                "additionalProperties": {}
              }
            },
            "required": [
              "id",
              "workout"
            ],
            "additionalProperties": {}
          },
          "description": "Latest 5, with the workout"
        }
      },
      "required": [
        "id",
        "name",
        "email",
        "avatar",
        "bio",
        "phone",
        "dateOfBirth",
        "height",
        "createdAt",
        "metricEntries",
        "nutritionEntries",
        "assignedTasks",
        "assignedWorkoutsClient"
      ],
      "additionalProperties": {}
    }
  • GET/api/clients/{id}/compliance

    Planned sessions done over the last 7, 30 and 90 days, and whether it is dropping

    Parameters
    NameLocationRequiredDescription
    idpathYes
    includequeryNo
    X-TimezoneheaderNoIANA time zone that decides "today", for example Europe/Skopje
    Response 200ComplianceResponse
    {
      "type": "object",
      "properties": {
        "windows": {
          "type": "object",
          "properties": {
            "d7": {
              "type": "object",
              "properties": {
                "pct": {
                  "anyOf": [
                    {
                      "type": "integer",
                      "minimum": 0,
                      "maximum": 100
                    },
                    {
                      "type": "null"
                    }
                  ],
                  "description": "null when nothing was planned"
                },
                "completed": {
                  "type": "integer",
                  "minimum": 0,
                  "maximum": 9007199254740991
                },
                "scheduled": {
                  "type": "integer",
                  "minimum": 0,
                  "maximum": 9007199254740991
                },
                "parts": {
                  "type": "object",
                  "properties": {
                    "training": {
                      "type": "object",
                      "properties": {
                        "pct": {
                          "anyOf": [
                            {
                              "type": "integer",
                              "minimum": 0,
                              "maximum": 100
                            },
                            {
                              "type": "null"
                            }
                          ],
                          "description": "null when nothing was planned"
                        },
                        "completed": {
                          "type": "integer",
                          "minimum": 0,
                          "maximum": 9007199254740991
                        },
                        "scheduled": {
                          "type": "integer",
                          "minimum": 0,
                          "maximum": 9007199254740991
                        }
                      },
                      "required": [
                        "pct",
                        "completed",
                        "scheduled"
                      ],
                      "additionalProperties": false
                    },
                    "nutrition": {
                      "type": "object",
                      "properties": {
                        "pct": {
                          "anyOf": [
                            {
                              "type": "integer",
                              "minimum": 0,
                              "maximum": 100
                            },
                            {
                              "type": "null"
                            }
                          ],
                          "description": "null when nothing was planned"
                        },
                        "completed": {
                          "type": "integer",
                          "minimum": 0,
                          "maximum": 9007199254740991
                        },
                        "scheduled": {
                          "type": "integer",
                          "minimum": 0,
                          "maximum": 9007199254740991
                        }
                      },
                      "required": [
                        "pct",
                        "completed",
                        "scheduled"
                      ],
                      "additionalProperties": false
                    },
                    "habits": {
                      "type": "object",
                      "properties": {
                        "pct": {
                          "anyOf": [
                            {
                              "type": "integer",
                              "minimum": 0,
                              "maximum": 100
                            },
                            {
                              "type": "null"
                            }
                          ],
                          "description": "null when nothing was planned"
                        },
                        "completed": {
                          "type": "integer",
                          "minimum": 0,
                          "maximum": 9007199254740991
                        },
                        "scheduled": {
                          "type": "integer",
                          "minimum": 0,
                          "maximum": 9007199254740991
                        }
                      },
                      "required": [
                        "pct",
                        "completed",
                        "scheduled"
                      ],
                      "additionalProperties": false
                    }
                  },
                  "required": [
                    "training"
                  ],
                  "additionalProperties": false
                }
              },
              "required": [
                "pct",
                "completed",
                "scheduled",
                "parts"
              ],
              "additionalProperties": false
            },
            "d30": {
              "type": "object",
              "properties": {
                "pct": {
                  "anyOf": [
                    {
                      "type": "integer",
                      "minimum": 0,
                      "maximum": 100
                    },
                    {
                      "type": "null"
                    }
                  ],
                  "description": "null when nothing was planned"
                },
                "completed": {
                  "type": "integer",
                  "minimum": 0,
                  "maximum": 9007199254740991
                },
                "scheduled": {
                  "type": "integer",
                  "minimum": 0,
                  "maximum": 9007199254740991
                },
                "parts": {
                  "type": "object",
                  "properties": {
                    "training": {
                      "type": "object",
                      "properties": {
                        "pct": {
                          "anyOf": [
                            {
                              "type": "integer",
                              "minimum": 0,
                              "maximum": 100
                            },
                            {
                              "type": "null"
                            }
                          ],
                          "description": "null when nothing was planned"
                        },
                        "completed": {
                          "type": "integer",
                          "minimum": 0,
                          "maximum": 9007199254740991
                        },
                        "scheduled": {
                          "type": "integer",
                          "minimum": 0,
                          "maximum": 9007199254740991
                        }
                      },
                      "required": [
                        "pct",
                        "completed",
                        "scheduled"
                      ],
                      "additionalProperties": false
                    },
                    "nutrition": {
                      "type": "object",
                      "properties": {
                        "pct": {
                          "anyOf": [
                            {
                              "type": "integer",
                              "minimum": 0,
                              "maximum": 100
                            },
                            {
                              "type": "null"
                            }
                          ],
                          "description": "null when nothing was planned"
                        },
                        "completed": {
                          "type": "integer",
                          "minimum": 0,
                          "maximum": 9007199254740991
                        },
                        "scheduled": {
                          "type": "integer",
                          "minimum": 0,
                          "maximum": 9007199254740991
                        }
                      },
                      "required": [
                        "pct",
                        "completed",
                        "scheduled"
                      ],
                      "additionalProperties": false
                    },
                    "habits": {
                      "type": "object",
                      "properties": {
                        "pct": {
                          "anyOf": [
                            {
                              "type": "integer",
                              "minimum": 0,
                              "maximum": 100
                            },
                            {
                              "type": "null"
                            }
                          ],
                          "description": "null when nothing was planned"
                        },
                        "completed": {
                          "type": "integer",
                          "minimum": 0,
                          "maximum": 9007199254740991
                        },
                        "scheduled": {
                          "type": "integer",
                          "minimum": 0,
                          "maximum": 9007199254740991
                        }
                      },
                      "required": [
                        "pct",
                        "completed",
                        "scheduled"
                      ],
                      "additionalProperties": false
                    }
                  },
                  "required": [
                    "training"
                  ],
                  "additionalProperties": false
                }
              },
              "required": [
                "pct",
                "completed",
                "scheduled",
                "parts"
              ],
              "additionalProperties": false
            },
            "d90": {
              "type": "object",
              "properties": {
                "pct": {
                  "anyOf": [
                    {
                      "type": "integer",
                      "minimum": 0,
                      "maximum": 100
                    },
                    {
                      "type": "null"
                    }
                  ],
                  "description": "null when nothing was planned"
                },
                "completed": {
                  "type": "integer",
                  "minimum": 0,
                  "maximum": 9007199254740991
                },
                "scheduled": {
                  "type": "integer",
                  "minimum": 0,
                  "maximum": 9007199254740991
                },
                "parts": {
                  "type": "object",
                  "properties": {
                    "training": {
                      "type": "object",
                      "properties": {
                        "pct": {
                          "anyOf": [
                            {
                              "type": "integer",
                              "minimum": 0,
                              "maximum": 100
                            },
                            {
                              "type": "null"
                            }
                          ],
                          "description": "null when nothing was planned"
                        },
                        "completed": {
                          "type": "integer",
                          "minimum": 0,
                          "maximum": 9007199254740991
                        },
                        "scheduled": {
                          "type": "integer",
                          "minimum": 0,
                          "maximum": 9007199254740991
                        }
                      },
                      "required": [
                        "pct",
                        "completed",
                        "scheduled"
                      ],
                      "additionalProperties": false
                    },
                    "nutrition": {
                      "type": "object",
                      "properties": {
                        "pct": {
                          "anyOf": [
                            {
                              "type": "integer",
                              "minimum": 0,
                              "maximum": 100
                            },
                            {
                              "type": "null"
                            }
                          ],
                          "description": "null when nothing was planned"
                        },
                        "completed": {
                          "type": "integer",
                          "minimum": 0,
                          "maximum": 9007199254740991
                        },
                        "scheduled": {
                          "type": "integer",
                          "minimum": 0,
                          "maximum": 9007199254740991
                        }
                      },
                      "required": [
                        "pct",
                        "completed",
                        "scheduled"
                      ],
                      "additionalProperties": false
                    },
                    "habits": {
                      "type": "object",
                      "properties": {
                        "pct": {
                          "anyOf": [
                            {
                              "type": "integer",
                              "minimum": 0,
                              "maximum": 100
                            },
                            {
                              "type": "null"
                            }
                          ],
                          "description": "null when nothing was planned"
                        },
                        "completed": {
                          "type": "integer",
                          "minimum": 0,
                          "maximum": 9007199254740991
                        },
                        "scheduled": {
                          "type": "integer",
                          "minimum": 0,
                          "maximum": 9007199254740991
                        }
                      },
                      "required": [
                        "pct",
                        "completed",
                        "scheduled"
                      ],
                      "additionalProperties": false
                    }
                  },
                  "required": [
                    "training"
                  ],
                  "additionalProperties": false
                }
              },
              "required": [
                "pct",
                "completed",
                "scheduled",
                "parts"
              ],
              "additionalProperties": false
            }
          },
          "required": [
            "d7",
            "d30",
            "d90"
          ],
          "additionalProperties": false,
          "description": "Each window ends yesterday"
        },
        "previous": {
          "type": "object",
          "properties": {
            "d7": {
              "type": "object",
              "properties": {
                "pct": {
                  "anyOf": [
                    {
                      "type": "integer",
                      "minimum": 0,
                      "maximum": 100
                    },
                    {
                      "type": "null"
                    }
                  ],
                  "description": "null when nothing was planned"
                },
                "completed": {
                  "type": "integer",
                  "minimum": 0,
                  "maximum": 9007199254740991
                },
                "scheduled": {
                  "type": "integer",
                  "minimum": 0,
                  "maximum": 9007199254740991
                },
                "parts": {
                  "type": "object",
                  "properties": {
                    "training": {
                      "type": "object",
                      "properties": {
                        "pct": {
                          "anyOf": [
                            {
                              "type": "integer",
                              "minimum": 0,
                              "maximum": 100
                            },
                            {
                              "type": "null"
                            }
                          ],
                          "description": "null when nothing was planned"
                        },
                        "completed": {
                          "type": "integer",
                          "minimum": 0,
                          "maximum": 9007199254740991
                        },
                        "scheduled": {
                          "type": "integer",
                          "minimum": 0,
                          "maximum": 9007199254740991
                        }
                      },
                      "required": [
                        "pct",
                        "completed",
                        "scheduled"
                      ],
                      "additionalProperties": false
                    },
                    "nutrition": {
                      "type": "object",
                      "properties": {
                        "pct": {
                          "anyOf": [
                            {
                              "type": "integer",
                              "minimum": 0,
                              "maximum": 100
                            },
                            {
                              "type": "null"
                            }
                          ],
                          "description": "null when nothing was planned"
                        },
                        "completed": {
                          "type": "integer",
                          "minimum": 0,
                          "maximum": 9007199254740991
                        },
                        "scheduled": {
                          "type": "integer",
                          "minimum": 0,
                          "maximum": 9007199254740991
                        }
                      },
                      "required": [
                        "pct",
                        "completed",
                        "scheduled"
                      ],
                      "additionalProperties": false
                    },
                    "habits": {
                      "type": "object",
                      "properties": {
                        "pct": {
                          "anyOf": [
                            {
                              "type": "integer",
                              "minimum": 0,
                              "maximum": 100
                            },
                            {
                              "type": "null"
                            }
                          ],
                          "description": "null when nothing was planned"
                        },
                        "completed": {
                          "type": "integer",
                          "minimum": 0,
                          "maximum": 9007199254740991
                        },
                        "scheduled": {
                          "type": "integer",
                          "minimum": 0,
                          "maximum": 9007199254740991
                        }
                      },
                      "required": [
                        "pct",
                        "completed",
                        "scheduled"
                      ],
                      "additionalProperties": false
                    }
                  },
                  "required": [
                    "training"
                  ],
                  "additionalProperties": false
                }
              },
              "required": [
                "pct",
                "completed",
                "scheduled",
                "parts"
              ],
              "additionalProperties": false
            },
            "d30": {
              "type": "object",
              "properties": {
                "pct": {
                  "anyOf": [
                    {
                      "type": "integer",
                      "minimum": 0,
                      "maximum": 100
                    },
                    {
                      "type": "null"
                    }
                  ],
                  "description": "null when nothing was planned"
                },
                "completed": {
                  "type": "integer",
                  "minimum": 0,
                  "maximum": 9007199254740991
                },
                "scheduled": {
                  "type": "integer",
                  "minimum": 0,
                  "maximum": 9007199254740991
                },
                "parts": {
                  "type": "object",
                  "properties": {
                    "training": {
                      "type": "object",
                      "properties": {
                        "pct": {
                          "anyOf": [
                            {
                              "type": "integer",
                              "minimum": 0,
                              "maximum": 100
                            },
                            {
                              "type": "null"
                            }
                          ],
                          "description": "null when nothing was planned"
                        },
                        "completed": {
                          "type": "integer",
                          "minimum": 0,
                          "maximum": 9007199254740991
                        },
                        "scheduled": {
                          "type": "integer",
                          "minimum": 0,
                          "maximum": 9007199254740991
                        }
                      },
                      "required": [
                        "pct",
                        "completed",
                        "scheduled"
                      ],
                      "additionalProperties": false
                    },
                    "nutrition": {
                      "type": "object",
                      "properties": {
                        "pct": {
                          "anyOf": [
                            {
                              "type": "integer",
                              "minimum": 0,
                              "maximum": 100
                            },
                            {
                              "type": "null"
                            }
                          ],
                          "description": "null when nothing was planned"
                        },
                        "completed": {
                          "type": "integer",
                          "minimum": 0,
                          "maximum": 9007199254740991
                        },
                        "scheduled": {
                          "type": "integer",
                          "minimum": 0,
                          "maximum": 9007199254740991
                        }
                      },
                      "required": [
                        "pct",
                        "completed",
                        "scheduled"
                      ],
                      "additionalProperties": false
                    },
                    "habits": {
                      "type": "object",
                      "properties": {
                        "pct": {
                          "anyOf": [
                            {
                              "type": "integer",
                              "minimum": 0,
                              "maximum": 100
                            },
                            {
                              "type": "null"
                            }
                          ],
                          "description": "null when nothing was planned"
                        },
                        "completed": {
                          "type": "integer",
                          "minimum": 0,
                          "maximum": 9007199254740991
                        },
                        "scheduled": {
                          "type": "integer",
                          "minimum": 0,
                          "maximum": 9007199254740991
                        }
                      },
                      "required": [
                        "pct",
                        "completed",
                        "scheduled"
                      ],
                      "additionalProperties": false
                    }
                  },
                  "required": [
                    "training"
                  ],
                  "additionalProperties": false
                }
              },
              "required": [
                "pct",
                "completed",
                "scheduled",
                "parts"
              ],
              "additionalProperties": false
            },
            "d90": {
              "type": "object",
              "properties": {
                "pct": {
                  "anyOf": [
                    {
                      "type": "integer",
                      "minimum": 0,
                      "maximum": 100
                    },
                    {
                      "type": "null"
                    }
                  ],
                  "description": "null when nothing was planned"
                },
                "completed": {
                  "type": "integer",
                  "minimum": 0,
                  "maximum": 9007199254740991
                },
                "scheduled": {
                  "type": "integer",
                  "minimum": 0,
                  "maximum": 9007199254740991
                },
                "parts": {
                  "type": "object",
                  "properties": {
                    "training": {
                      "type": "object",
                      "properties": {
                        "pct": {
                          "anyOf": [
                            {
                              "type": "integer",
                              "minimum": 0,
                              "maximum": 100
                            },
                            {
                              "type": "null"
                            }
                          ],
                          "description": "null when nothing was planned"
                        },
                        "completed": {
                          "type": "integer",
                          "minimum": 0,
                          "maximum": 9007199254740991
                        },
                        "scheduled": {
                          "type": "integer",
                          "minimum": 0,
                          "maximum": 9007199254740991
                        }
                      },
                      "required": [
                        "pct",
                        "completed",
                        "scheduled"
                      ],
                      "additionalProperties": false
                    },
                    "nutrition": {
                      "type": "object",
                      "properties": {
                        "pct": {
                          "anyOf": [
                            {
                              "type": "integer",
                              "minimum": 0,
                              "maximum": 100
                            },
                            {
                              "type": "null"
                            }
                          ],
                          "description": "null when nothing was planned"
                        },
                        "completed": {
                          "type": "integer",
                          "minimum": 0,
                          "maximum": 9007199254740991
                        },
                        "scheduled": {
                          "type": "integer",
                          "minimum": 0,
                          "maximum": 9007199254740991
                        }
                      },
                      "required": [
                        "pct",
                        "completed",
                        "scheduled"
                      ],
                      "additionalProperties": false
                    },
                    "habits": {
                      "type": "object",
                      "properties": {
                        "pct": {
                          "anyOf": [
                            {
                              "type": "integer",
                              "minimum": 0,
                              "maximum": 100
                            },
                            {
                              "type": "null"
                            }
                          ],
                          "description": "null when nothing was planned"
                        },
                        "completed": {
                          "type": "integer",
                          "minimum": 0,
                          "maximum": 9007199254740991
                        },
                        "scheduled": {
                          "type": "integer",
                          "minimum": 0,
                          "maximum": 9007199254740991
                        }
                      },
                      "required": [
                        "pct",
                        "completed",
                        "scheduled"
                      ],
                      "additionalProperties": false
                    }
                  },
                  "required": [
                    "training"
                  ],
                  "additionalProperties": false
                }
              },
              "required": [
                "pct",
                "completed",
                "scheduled",
                "parts"
              ],
              "additionalProperties": false
            }
          },
          "required": [
            "d7",
            "d30",
            "d90"
          ],
          "additionalProperties": false,
          "description": "The same windows, shifted back by their length"
        },
        "dropFlag": {
          "type": "boolean",
          "description": "The 30-day figure fell 20 points or more"
        },
        "dropPct": {
          "type": "integer",
          "minimum": 0,
          "maximum": 9007199254740991,
          "description": "Points the 30-day figure fell by; 0 when it held"
        }
      },
      "required": [
        "windows",
        "previous",
        "dropFlag",
        "dropPct"
      ],
      "additionalProperties": false
    }
  • GET/api/readiness

    How ready to train a client is today, from sleep, mood and energy and training load

    Parameters
    NameLocationRequiredDescription
    clientIdqueryNo
    datequeryNo
    X-TimezoneheaderNoIANA time zone that decides "today", for example Europe/Skopje
    Response 200ReadinessResponse
    {
      "type": "object",
      "properties": {
        "date": {
          "type": "string"
        },
        "score": {
          "anyOf": [
            {
              "type": "integer",
              "minimum": 0,
              "maximum": 100
            },
            {
              "type": "null"
            }
          ],
          "description": "null without sleep or a check-in"
        },
        "band": {
          "anyOf": [
            {
              "type": "string",
              "enum": [
                "low",
                "medium",
                "high"
              ]
            },
            {
              "type": "null"
            }
          ]
        },
        "inputs": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "key": {
                "type": "string",
                "enum": [
                  "sleep",
                  "wellbeing",
                  "load",
                  "hrv"
                ]
              },
              "value": {
                "type": "number"
              },
              "contribution": {
                "type": "number",
                "description": "Points of the score; the contributions add up to it"
              }
            },
            "required": [
              "key",
              "value",
              "contribution"
            ],
            "additionalProperties": false
          }
        },
        "missing": {
          "type": "array",
          "items": {
            "type": "string",
            "enum": [
              "sleep",
              "wellbeing",
              "load",
              "hrv"
            ]
          }
        }
      },
      "required": [
        "date",
        "score",
        "band",
        "inputs",
        "missing"
      ],
      "additionalProperties": false
    }

Workouts

  • GET/api/assigned-workouts

    Workouts assigned to clients (the item count, not the items)

    Parameters
    NameLocationRequiredDescription
    clientIdqueryNoOnly this client (a coach must coach them)
    statusqueryNoOnly this status, for example completed
    limitqueryNoRows to return (default 500, at most 2000)
    Response 200AssignedWorkoutListResponse
    {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "workoutId": {
            "type": "string"
          },
          "clientId": {
            "type": "string"
          },
          "trainerId": {
            "type": "string"
          },
          "scheduledDate": {
            "anyOf": [
              {
                "type": "string",
                "description": "Calendar day, YYYY-MM-DD"
              },
              {
                "type": "null"
              }
            ]
          },
          "status": {
            "type": "string"
          },
          "clientNotes": {
            "type": [
              "string",
              "null"
            ]
          },
          "completedAt": {
            "anyOf": [
              {
                "type": "string",
                "description": "ISO-8601 timestamp"
              },
              {
                "type": "null"
              }
            ]
          },
          "programmeAssignmentId": {
            "type": [
              "string",
              "null"
            ]
          },
          "createdAt": {
            "type": "string",
            "description": "ISO-8601 timestamp"
          },
          "updatedAt": {
            "type": "string",
            "description": "ISO-8601 timestamp"
          },
          "client": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "avatar": {
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "required": [
              "id",
              "name",
              "avatar"
            ],
            "additionalProperties": {}
          },
          "trainer": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "avatar": {
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "required": [
              "id",
              "name",
              "avatar"
            ],
            "additionalProperties": {}
          },
          "workout": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "description": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "category": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "difficulty": {
                "type": "string"
              },
              "estimatedDuration": {
                "anyOf": [
                  {
                    "type": "integer",
                    "minimum": -9007199254740991,
                    "maximum": 9007199254740991
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "_count": {
                "type": "object",
                "properties": {
                  "items": {
                    "type": "integer",
                    "minimum": -9007199254740991,
                    "maximum": 9007199254740991
                  }
                },
                "required": [
                  "items"
                ],
                "additionalProperties": false
              }
            },
            "required": [
              "id",
              "name",
              "description",
              "category",
              "difficulty",
              "estimatedDuration",
              "_count"
            ],
            "additionalProperties": {}
          }
        },
        "required": [
          "id",
          "workoutId",
          "clientId",
          "trainerId",
          "scheduledDate",
          "status",
          "clientNotes",
          "completedAt",
          "programmeAssignmentId",
          "createdAt",
          "updatedAt",
          "client",
          "trainer",
          "workout"
        ],
        "additionalProperties": {}
      }
    }
  • GET/api/assigned-workouts/{id}

    One assignment with the full prescription

    Parameters
    NameLocationRequiredDescription
    idpathYes
    Response 200AssignedWorkoutDetailResponse
    {
      "type": "object",
      "properties": {
        "id": {
          "type": "string"
        },
        "workoutId": {
          "type": "string"
        },
        "clientId": {
          "type": "string"
        },
        "trainerId": {
          "type": "string"
        },
        "scheduledDate": {
          "anyOf": [
            {
              "type": "string",
              "description": "Calendar day, YYYY-MM-DD"
            },
            {
              "type": "null"
            }
          ]
        },
        "status": {
          "type": "string"
        },
        "clientNotes": {
          "type": [
            "string",
            "null"
          ]
        },
        "completedAt": {
          "anyOf": [
            {
              "type": "string",
              "description": "ISO-8601 timestamp"
            },
            {
              "type": "null"
            }
          ]
        },
        "programmeAssignmentId": {
          "type": [
            "string",
            "null"
          ]
        },
        "createdAt": {
          "type": "string",
          "description": "ISO-8601 timestamp"
        },
        "updatedAt": {
          "type": "string",
          "description": "ISO-8601 timestamp"
        },
        "client": {
          "type": "object",
          "properties": {
            "id": {
              "type": "string"
            },
            "name": {
              "type": "string"
            },
            "avatar": {
              "type": [
                "string",
                "null"
              ]
            }
          },
          "required": [
            "id",
            "name",
            "avatar"
          ],
          "additionalProperties": {}
        },
        "trainer": {
          "type": "object",
          "properties": {
            "id": {
              "type": "string"
            },
            "name": {
              "type": "string"
            },
            "avatar": {
              "type": [
                "string",
                "null"
              ]
            }
          },
          "required": [
            "id",
            "name",
            "avatar"
          ],
          "additionalProperties": {}
        },
        "workout": {
          "type": "object",
          "properties": {
            "id": {
              "type": "string"
            },
            "trainerId": {
              "type": [
                "string",
                "null"
              ]
            },
            "name": {
              "type": "string"
            },
            "description": {
              "type": [
                "string",
                "null"
              ]
            },
            "category": {
              "type": [
                "string",
                "null"
              ]
            },
            "difficulty": {
              "type": "string"
            },
            "estimatedDuration": {
              "anyOf": [
                {
                  "type": "integer",
                  "minimum": -9007199254740991,
                  "maximum": 9007199254740991
                },
                {
                  "type": "null"
                }
              ],
              "description": "Minutes"
            },
            "isTemplate": {
              "type": "boolean"
            },
            "createdAt": {
              "type": "string",
              "description": "ISO-8601 timestamp"
            },
            "updatedAt": {
              "type": "string",
              "description": "ISO-8601 timestamp"
            },
            "items": {
              "type": "array",
              "items": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string"
                  },
                  "workoutId": {
                    "type": "string"
                  },
                  "exerciseId": {
                    "type": "string"
                  },
                  "sets": {
                    "anyOf": [
                      {
                        "type": "integer",
                        "minimum": -9007199254740991,
                        "maximum": 9007199254740991
                      },
                      {
                        "type": "null"
                      }
                    ]
                  },
                  "reps": {
                    "anyOf": [
                      {
                        "type": "integer",
                        "minimum": -9007199254740991,
                        "maximum": 9007199254740991
                      },
                      {
                        "type": "null"
                      }
                    ]
                  },
                  "weight": {
                    "type": [
                      "number",
                      "null"
                    ]
                  },
                  "restTime": {
                    "anyOf": [
                      {
                        "type": "integer",
                        "minimum": -9007199254740991,
                        "maximum": 9007199254740991
                      },
                      {
                        "type": "null"
                      }
                    ]
                  },
                  "duration": {
                    "anyOf": [
                      {
                        "type": "integer",
                        "minimum": -9007199254740991,
                        "maximum": 9007199254740991
                      },
                      {
                        "type": "null"
                      }
                    ]
                  },
                  "order": {
                    "type": "integer",
                    "minimum": -9007199254740991,
                    "maximum": 9007199254740991
                  },
                  "notes": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "exercise": {
                    "type": "object",
                    "properties": {
                      "id": {
                        "type": "string"
                      },
                      "name": {
                        "type": "string"
                      },
                      "category": {
                        "type": [
                          "string",
                          "null"
                        ]
                      }
                    },
                    "required": [
                      "id",
                      "name",
                      "category"
                    ],
                    "additionalProperties": {}
                  }
                },
                "required": [
                  "id",
                  "workoutId",
                  "exerciseId",
                  "sets",
                  "reps",
                  "weight",
                  "restTime",
                  "duration",
                  "order",
                  "notes",
                  "exercise"
                ],
                "additionalProperties": {}
              }
            }
          },
          "required": [
            "id",
            "trainerId",
            "name",
            "description",
            "category",
            "difficulty",
            "estimatedDuration",
            "isTemplate",
            "createdAt",
            "updatedAt",
            "items"
          ],
          "additionalProperties": {}
        }
      },
      "required": [
        "id",
        "workoutId",
        "clientId",
        "trainerId",
        "scheduledDate",
        "status",
        "clientNotes",
        "completedAt",
        "programmeAssignmentId",
        "createdAt",
        "updatedAt",
        "client",
        "trainer",
        "workout"
      ],
      "additionalProperties": {}
    }
  • GET/api/workout-logs

    Completed workout logs, a page at a time

    Parameters
    NameLocationRequiredDescription
    clientIdqueryNoWhose logs (default: the caller)
    includequeryNoComma-separated: setLog, feedback
    limitqueryNoRows to return (default 30, at most 200)
    cursorqueryNoThe `nextCursor` of the previous page
    Response 200WorkoutLogPage
    {
      "type": "object",
      "properties": {
        "items": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "clientId": {
                "type": "string"
              },
              "workoutId": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "name": {
                "type": "string"
              },
              "date": {
                "type": "string",
                "description": "Calendar day, YYYY-MM-DD"
              },
              "duration": {
                "anyOf": [
                  {
                    "type": "integer",
                    "minimum": -9007199254740991,
                    "maximum": 9007199254740991
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "notes": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "calories": {
                "anyOf": [
                  {
                    "type": "integer",
                    "minimum": -9007199254740991,
                    "maximum": 9007199254740991
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "createdAt": {
                "type": "string",
                "description": "ISO-8601 timestamp"
              },
              "updatedAt": {
                "type": "string",
                "description": "ISO-8601 timestamp"
              },
              "exercises": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "exerciseId": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "exerciseName": {
                      "type": "string"
                    },
                    "sets": {
                      "anyOf": [
                        {
                          "type": "integer",
                          "minimum": -9007199254740991,
                          "maximum": 9007199254740991
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "reps": {
                      "anyOf": [
                        {
                          "type": "integer",
                          "minimum": -9007199254740991,
                          "maximum": 9007199254740991
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "weight": {
                      "type": [
                        "number",
                        "null"
                      ]
                    },
                    "duration": {
                      "anyOf": [
                        {
                          "type": "integer",
                          "minimum": -9007199254740991,
                          "maximum": 9007199254740991
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "order": {
                      "type": "integer",
                      "minimum": -9007199254740991,
                      "maximum": 9007199254740991
                    },
                    "setLog": {
                      "description": "With `?include=setLog`",
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  },
                  "required": [
                    "id",
                    "exerciseId",
                    "exerciseName",
                    "sets",
                    "reps",
                    "weight",
                    "duration",
                    "order"
                  ],
                  "additionalProperties": {}
                }
              }
            },
            "required": [
              "id",
              "clientId",
              "workoutId",
              "name",
              "date",
              "duration",
              "notes",
              "calories",
              "createdAt",
              "updatedAt",
              "exercises"
            ],
            "additionalProperties": {}
          }
        },
        "nextCursor": {
          "description": "Pass as `?cursor=` for the next page; null on the last",
          "type": [
            "string",
            "null"
          ]
        }
      },
      "required": [
        "items",
        "nextCursor"
      ],
      "additionalProperties": false
    }
  • GET/api/workout-logs/{id}

    One workout log with its exercises

    Parameters
    NameLocationRequiredDescription
    idpathYes
    Response 200WorkoutLog
    {
      "type": "object",
      "properties": {
        "id": {
          "type": "string"
        },
        "clientId": {
          "type": "string"
        },
        "workoutId": {
          "type": [
            "string",
            "null"
          ]
        },
        "name": {
          "type": "string"
        },
        "date": {
          "type": "string",
          "description": "Calendar day, YYYY-MM-DD"
        },
        "duration": {
          "anyOf": [
            {
              "type": "integer",
              "minimum": -9007199254740991,
              "maximum": 9007199254740991
            },
            {
              "type": "null"
            }
          ]
        },
        "notes": {
          "type": [
            "string",
            "null"
          ]
        },
        "calories": {
          "anyOf": [
            {
              "type": "integer",
              "minimum": -9007199254740991,
              "maximum": 9007199254740991
            },
            {
              "type": "null"
            }
          ]
        },
        "createdAt": {
          "type": "string",
          "description": "ISO-8601 timestamp"
        },
        "updatedAt": {
          "type": "string",
          "description": "ISO-8601 timestamp"
        },
        "exercises": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "exerciseId": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "exerciseName": {
                "type": "string"
              },
              "sets": {
                "anyOf": [
                  {
                    "type": "integer",
                    "minimum": -9007199254740991,
                    "maximum": 9007199254740991
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "reps": {
                "anyOf": [
                  {
                    "type": "integer",
                    "minimum": -9007199254740991,
                    "maximum": 9007199254740991
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "weight": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "duration": {
                "anyOf": [
                  {
                    "type": "integer",
                    "minimum": -9007199254740991,
                    "maximum": 9007199254740991
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "order": {
                "type": "integer",
                "minimum": -9007199254740991,
                "maximum": 9007199254740991
              },
              "setLog": {
                "description": "With `?include=setLog`",
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "required": [
              "id",
              "exerciseId",
              "exerciseName",
              "sets",
              "reps",
              "weight",
              "duration",
              "order"
            ],
            "additionalProperties": {}
          }
        }
      },
      "required": [
        "id",
        "clientId",
        "workoutId",
        "name",
        "date",
        "duration",
        "notes",
        "calories",
        "createdAt",
        "updatedAt",
        "exercises"
      ],
      "additionalProperties": {}
    }
  • GET/api/workouts

    The coach's workout templates with their exercises

    Parameters
    NameLocationRequiredDescription
    categoryqueryNoOnly this category
    limitqueryNoRows to return (default 500, at most 2000)
    Response 200WorkoutListResponse
    {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "trainerId": {
            "type": [
              "string",
              "null"
            ]
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "category": {
            "type": [
              "string",
              "null"
            ]
          },
          "difficulty": {
            "type": "string"
          },
          "estimatedDuration": {
            "anyOf": [
              {
                "type": "integer",
                "minimum": -9007199254740991,
                "maximum": 9007199254740991
              },
              {
                "type": "null"
              }
            ],
            "description": "Minutes"
          },
          "isTemplate": {
            "type": "boolean"
          },
          "createdAt": {
            "type": "string",
            "description": "ISO-8601 timestamp"
          },
          "updatedAt": {
            "type": "string",
            "description": "ISO-8601 timestamp"
          },
          "items": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string"
                },
                "workoutId": {
                  "type": "string"
                },
                "exerciseId": {
                  "type": "string"
                },
                "sets": {
                  "anyOf": [
                    {
                      "type": "integer",
                      "minimum": -9007199254740991,
                      "maximum": 9007199254740991
                    },
                    {
                      "type": "null"
                    }
                  ]
                },
                "reps": {
                  "anyOf": [
                    {
                      "type": "integer",
                      "minimum": -9007199254740991,
                      "maximum": 9007199254740991
                    },
                    {
                      "type": "null"
                    }
                  ]
                },
                "weight": {
                  "type": [
                    "number",
                    "null"
                  ]
                },
                "restTime": {
                  "anyOf": [
                    {
                      "type": "integer",
                      "minimum": -9007199254740991,
                      "maximum": 9007199254740991
                    },
                    {
                      "type": "null"
                    }
                  ]
                },
                "duration": {
                  "anyOf": [
                    {
                      "type": "integer",
                      "minimum": -9007199254740991,
                      "maximum": 9007199254740991
                    },
                    {
                      "type": "null"
                    }
                  ]
                },
                "order": {
                  "type": "integer",
                  "minimum": -9007199254740991,
                  "maximum": 9007199254740991
                },
                "notes": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "exercise": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    },
                    "category": {
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  },
                  "required": [
                    "id",
                    "name",
                    "category"
                  ],
                  "additionalProperties": {}
                }
              },
              "required": [
                "id",
                "workoutId",
                "exerciseId",
                "sets",
                "reps",
                "weight",
                "restTime",
                "duration",
                "order",
                "notes",
                "exercise"
              ],
              "additionalProperties": {}
            }
          }
        },
        "required": [
          "id",
          "trainerId",
          "name",
          "description",
          "category",
          "difficulty",
          "estimatedDuration",
          "isTemplate",
          "createdAt",
          "updatedAt",
          "items"
        ],
        "additionalProperties": {}
      }
    }
  • GET/api/workouts/{id}

    One workout with its items in order

    Parameters
    NameLocationRequiredDescription
    idpathYes
    Response 200Workout
    {
      "type": "object",
      "properties": {
        "id": {
          "type": "string"
        },
        "trainerId": {
          "type": [
            "string",
            "null"
          ]
        },
        "name": {
          "type": "string"
        },
        "description": {
          "type": [
            "string",
            "null"
          ]
        },
        "category": {
          "type": [
            "string",
            "null"
          ]
        },
        "difficulty": {
          "type": "string"
        },
        "estimatedDuration": {
          "anyOf": [
            {
              "type": "integer",
              "minimum": -9007199254740991,
              "maximum": 9007199254740991
            },
            {
              "type": "null"
            }
          ],
          "description": "Minutes"
        },
        "isTemplate": {
          "type": "boolean"
        },
        "createdAt": {
          "type": "string",
          "description": "ISO-8601 timestamp"
        },
        "updatedAt": {
          "type": "string",
          "description": "ISO-8601 timestamp"
        },
        "items": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "workoutId": {
                "type": "string"
              },
              "exerciseId": {
                "type": "string"
              },
              "sets": {
                "anyOf": [
                  {
                    "type": "integer",
                    "minimum": -9007199254740991,
                    "maximum": 9007199254740991
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "reps": {
                "anyOf": [
                  {
                    "type": "integer",
                    "minimum": -9007199254740991,
                    "maximum": 9007199254740991
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "weight": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "restTime": {
                "anyOf": [
                  {
                    "type": "integer",
                    "minimum": -9007199254740991,
                    "maximum": 9007199254740991
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "duration": {
                "anyOf": [
                  {
                    "type": "integer",
                    "minimum": -9007199254740991,
                    "maximum": 9007199254740991
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "order": {
                "type": "integer",
                "minimum": -9007199254740991,
                "maximum": 9007199254740991
              },
              "notes": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "exercise": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string"
                  },
                  "name": {
                    "type": "string"
                  },
                  "category": {
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                },
                "required": [
                  "id",
                  "name",
                  "category"
                ],
                "additionalProperties": {}
              }
            },
            "required": [
              "id",
              "workoutId",
              "exerciseId",
              "sets",
              "reps",
              "weight",
              "restTime",
              "duration",
              "order",
              "notes",
              "exercise"
            ],
            "additionalProperties": {}
          }
        }
      },
      "required": [
        "id",
        "trainerId",
        "name",
        "description",
        "category",
        "difficulty",
        "estimatedDuration",
        "isTemplate",
        "createdAt",
        "updatedAt",
        "items"
      ],
      "additionalProperties": {}
    }

Bookings

  • GET/api/booking/public-page

    The coach's public booking page settings (coach only); the page is /book/<slug>

    Response 200PublicBookingPageSettings
    {
      "type": "object",
      "properties": {
        "enabled": {
          "type": "boolean"
        },
        "slug": {
          "type": [
            "string",
            "null"
          ]
        }
      },
      "required": [
        "enabled",
        "slug"
      ],
      "additionalProperties": false
    }
  • PUT/api/booking/public-page

    Turn the public booking page on or off, or change its slug (coach only)

    Request bodyPublicBookingPageUpdate
    {
      "type": "object",
      "properties": {
        "enabled": {
          "type": "boolean"
        },
        "slug": {
          "type": "string",
          "minLength": 3,
          "maxLength": 40,
          "pattern": "^[a-z0-9]+(-[a-z0-9]+)*$"
        }
      },
      "additionalProperties": false
    }
    Response 200PublicBookingPageSettings
    {
      "type": "object",
      "properties": {
        "enabled": {
          "type": "boolean"
        },
        "slug": {
          "type": [
            "string",
            "null"
          ]
        }
      },
      "required": [
        "enabled",
        "slug"
      ],
      "additionalProperties": false
    }
  • GET/api/booking/waitlist

    Live 1:1 waitlist places: a client's own, or every place in a coach's diary

    Response 200BookingWaitlistListResponse
    {
      "type": "object",
      "properties": {
        "items": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "trainerId": {
                "type": "string"
              },
              "clientId": {
                "type": "string"
              },
              "sessionTypeId": {
                "type": "string"
              },
              "startsAt": {
                "type": "string",
                "format": "date-time",
                "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
              },
              "status": {
                "type": "string",
                "enum": [
                  "waiting",
                  "offered",
                  "booked",
                  "expired",
                  "left"
                ]
              },
              "offeredAt": {
                "anyOf": [
                  {
                    "type": "string",
                    "format": "date-time",
                    "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "holdExpiresAt": {
                "anyOf": [
                  {
                    "type": "string",
                    "format": "date-time",
                    "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "createdAt": {
                "type": "string",
                "format": "date-time",
                "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
              },
              "position": {
                "anyOf": [
                  {
                    "type": "integer",
                    "exclusiveMinimum": 0,
                    "maximum": 9007199254740991
                  },
                  {
                    "type": "null"
                  }
                ]
              }
            },
            "required": [
              "id",
              "trainerId",
              "clientId",
              "sessionTypeId",
              "startsAt",
              "status",
              "offeredAt",
              "holdExpiresAt",
              "createdAt",
              "position"
            ],
            "additionalProperties": false
          }
        }
      },
      "required": [
        "items"
      ],
      "additionalProperties": false
    }
  • POST/api/booking/waitlist

    A client queues for a taken start of one of their coach's session types

    Request bodyBookingWaitlistJoin
    {
      "type": "object",
      "properties": {
        "sessionTypeId": {
          "type": "string",
          "pattern": "^[cC][^\\s-]{8,}$"
        },
        "startsAt": {
          "type": "string",
          "format": "date-time",
          "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
        }
      },
      "required": [
        "sessionTypeId",
        "startsAt"
      ],
      "additionalProperties": false
    }
    Response 201BookingWaitlistPlace
    {
      "type": "object",
      "properties": {
        "id": {
          "type": "string"
        },
        "trainerId": {
          "type": "string"
        },
        "clientId": {
          "type": "string"
        },
        "sessionTypeId": {
          "type": "string"
        },
        "startsAt": {
          "type": "string",
          "format": "date-time",
          "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
        },
        "status": {
          "type": "string",
          "enum": [
            "waiting",
            "offered",
            "booked",
            "expired",
            "left"
          ]
        },
        "offeredAt": {
          "anyOf": [
            {
              "type": "string",
              "format": "date-time",
              "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
            },
            {
              "type": "null"
            }
          ]
        },
        "holdExpiresAt": {
          "anyOf": [
            {
              "type": "string",
              "format": "date-time",
              "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
            },
            {
              "type": "null"
            }
          ]
        },
        "createdAt": {
          "type": "string",
          "format": "date-time",
          "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
        },
        "position": {
          "anyOf": [
            {
              "type": "integer",
              "exclusiveMinimum": 0,
              "maximum": 9007199254740991
            },
            {
              "type": "null"
            }
          ]
        }
      },
      "required": [
        "id",
        "trainerId",
        "clientId",
        "sessionTypeId",
        "startsAt",
        "status",
        "offeredAt",
        "holdExpiresAt",
        "createdAt",
        "position"
      ],
      "additionalProperties": false
    }
  • DELETE/api/booking/waitlist/{id}

    Leave a waitlist; an offer held is passed to the next in line

    Parameters
    NameLocationRequiredDescription
    idpathYes
  • GET/api/bookings

    Sessions overlapping a window: a coach's diary or a client's own

    Parameters
    NameLocationRequiredDescription
    fromqueryYes
    toqueryYes
    Response 200BookingListResponse
    {
      "maxItems": 1000,
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "startsAt": {
            "type": "string",
            "description": "ISO-8601 timestamp"
          },
          "endsAt": {
            "type": "string",
            "description": "ISO-8601 timestamp"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "confirmed",
              "cancelled",
              "no_show",
              "attended",
              "late_cancel"
            ]
          },
          "clientNote": {
            "type": [
              "string",
              "null"
            ]
          },
          "coachNote": {
            "description": "Coach only; absent for a client",
            "type": [
              "string",
              "null"
            ]
          },
          "notes": {
            "description": "Deprecated: read clientNote and coachNote",
            "type": [
              "string",
              "null"
            ]
          },
          "meetingUrl": {
            "type": [
              "string",
              "null"
            ]
          },
          "seriesId": {
            "type": [
              "string",
              "null"
            ]
          },
          "cancelReason": {
            "type": [
              "string",
              "null"
            ]
          },
          "createdAt": {
            "type": "string",
            "description": "ISO-8601 timestamp"
          },
          "sessionType": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "color": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "durationMin": {
                "type": "integer",
                "minimum": -9007199254740991,
                "maximum": 9007199254740991
              },
              "mode": {
                "type": "string"
              }
            },
            "required": [
              "id",
              "name",
              "color",
              "durationMin",
              "mode"
            ],
            "additionalProperties": {}
          },
          "trainer": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "avatar": {
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "required": [
              "id",
              "name",
              "avatar"
            ],
            "additionalProperties": {}
          },
          "client": {
            "anyOf": [
              {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string"
                  },
                  "name": {
                    "type": "string"
                  },
                  "avatar": {
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                },
                "required": [
                  "id",
                  "name",
                  "avatar"
                ],
                "additionalProperties": {}
              },
              {
                "type": "null"
              }
            ]
          },
          "location": {
            "anyOf": [
              {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string"
                  },
                  "name": {
                    "type": "string"
                  },
                  "address": {
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                },
                "required": [
                  "id",
                  "name",
                  "address"
                ],
                "additionalProperties": {}
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "id",
          "startsAt",
          "endsAt",
          "status",
          "clientNote",
          "notes",
          "meetingUrl",
          "seriesId",
          "cancelReason",
          "createdAt",
          "sessionType",
          "trainer",
          "client",
          "location"
        ],
        "additionalProperties": {}
      }
    }
  • GET/api/bookings/{id}

    One booking

    Parameters
    NameLocationRequiredDescription
    idpathYes
    Response 200BookingView
    {
      "type": "object",
      "properties": {
        "id": {
          "type": "string"
        },
        "startsAt": {
          "type": "string",
          "description": "ISO-8601 timestamp"
        },
        "endsAt": {
          "type": "string",
          "description": "ISO-8601 timestamp"
        },
        "status": {
          "type": "string",
          "enum": [
            "pending",
            "confirmed",
            "cancelled",
            "no_show",
            "attended",
            "late_cancel"
          ]
        },
        "clientNote": {
          "type": [
            "string",
            "null"
          ]
        },
        "coachNote": {
          "description": "Coach only; absent for a client",
          "type": [
            "string",
            "null"
          ]
        },
        "notes": {
          "description": "Deprecated: read clientNote and coachNote",
          "type": [
            "string",
            "null"
          ]
        },
        "meetingUrl": {
          "type": [
            "string",
            "null"
          ]
        },
        "seriesId": {
          "type": [
            "string",
            "null"
          ]
        },
        "cancelReason": {
          "type": [
            "string",
            "null"
          ]
        },
        "createdAt": {
          "type": "string",
          "description": "ISO-8601 timestamp"
        },
        "sessionType": {
          "type": "object",
          "properties": {
            "id": {
              "type": "string"
            },
            "name": {
              "type": "string"
            },
            "color": {
              "type": [
                "string",
                "null"
              ]
            },
            "durationMin": {
              "type": "integer",
              "minimum": -9007199254740991,
              "maximum": 9007199254740991
            },
            "mode": {
              "type": "string"
            }
          },
          "required": [
            "id",
            "name",
            "color",
            "durationMin",
            "mode"
          ],
          "additionalProperties": {}
        },
        "trainer": {
          "type": "object",
          "properties": {
            "id": {
              "type": "string"
            },
            "name": {
              "type": "string"
            },
            "avatar": {
              "type": [
                "string",
                "null"
              ]
            }
          },
          "required": [
            "id",
            "name",
            "avatar"
          ],
          "additionalProperties": {}
        },
        "client": {
          "anyOf": [
            {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string"
                },
                "name": {
                  "type": "string"
                },
                "avatar": {
                  "type": [
                    "string",
                    "null"
                  ]
                }
              },
              "required": [
                "id",
                "name",
                "avatar"
              ],
              "additionalProperties": {}
            },
            {
              "type": "null"
            }
          ]
        },
        "location": {
          "anyOf": [
            {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string"
                },
                "name": {
                  "type": "string"
                },
                "address": {
                  "type": [
                    "string",
                    "null"
                  ]
                }
              },
              "required": [
                "id",
                "name",
                "address"
              ],
              "additionalProperties": {}
            },
            {
              "type": "null"
            }
          ]
        }
      },
      "required": [
        "id",
        "startsAt",
        "endsAt",
        "status",
        "clientNote",
        "notes",
        "meetingUrl",
        "seriesId",
        "cancelReason",
        "createdAt",
        "sessionType",
        "trainer",
        "client",
        "location"
      ],
      "additionalProperties": {}
    }

Check-ins

  • GET/api/checkins/due

    Scheduled check-in occurrences and their status

    Parameters
    NameLocationRequiredDescription
    clientIdqueryNo
    statusqueryNo
    limitqueryNoRows to return (default 200, at most 2000)
    Response 200CheckinDueListResponse
    {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "scheduleId": {
            "type": "string"
          },
          "clientId": {
            "type": "string"
          },
          "dueDate": {
            "type": "string",
            "description": "Calendar day, YYYY-MM-DD"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "done",
              "missed",
              "skipped"
            ]
          },
          "formResponseId": {
            "type": [
              "string",
              "null"
            ]
          },
          "remindedAt": {
            "anyOf": [
              {
                "type": "string",
                "description": "ISO-8601 timestamp"
              },
              {
                "type": "null"
              }
            ]
          },
          "completedAt": {
            "anyOf": [
              {
                "type": "string",
                "description": "ISO-8601 timestamp"
              },
              {
                "type": "null"
              }
            ]
          },
          "schedule": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "cadence": {
                "type": "string",
                "enum": [
                  "once",
                  "weekly",
                  "biweekly",
                  "monthly"
                ]
              },
              "trainerId": {
                "type": "string"
              },
              "form": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string"
                  },
                  "title": {
                    "type": "string"
                  },
                  "description": {
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                },
                "required": [
                  "id",
                  "title",
                  "description"
                ],
                "additionalProperties": false
              }
            },
            "required": [
              "id",
              "cadence",
              "trainerId",
              "form"
            ],
            "additionalProperties": false
          }
        },
        "required": [
          "id",
          "scheduleId",
          "clientId",
          "dueDate",
          "status",
          "formResponseId",
          "remindedAt",
          "completedAt",
          "schedule"
        ],
        "additionalProperties": false
      }
    }
  • GET/api/form-responses

    Submitted check-in and form responses

    Parameters
    NameLocationRequiredDescription
    formIdqueryNoOnly responses to this form
    clientIdqueryNoOnly this client (ignored for a client, who sees their own)
    limitqueryNoRows to return (default 500, at most 2000)
    Response 200FormResponseListResponse
    {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "formId": {
            "type": "string"
          },
          "clientId": {
            "type": "string"
          },
          "answers": {
            "type": "string",
            "description": "JSON-encoded answers, keyed by question"
          },
          "submittedAt": {
            "type": "string",
            "description": "ISO-8601 timestamp"
          },
          "form": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "title": {
                "type": "string"
              }
            },
            "required": [
              "id",
              "title"
            ],
            "additionalProperties": {}
          },
          "client": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "avatar": {
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "required": [
              "id",
              "name",
              "avatar"
            ],
            "additionalProperties": {}
          }
        },
        "required": [
          "id",
          "formId",
          "clientId",
          "answers",
          "submittedAt",
          "form",
          "client"
        ],
        "additionalProperties": {}
      }
    }
  • POST/api/forms/{id}/sign

    E-sign your own submitted response to this form. Behind ESIGN_ENABLED: 404 while it is off

    Parameters
    NameLocationRequiredDescription
    idpathYes
    Request bodyFormSign
    {
      "type": "object",
      "properties": {
        "responseId": {
          "type": "string",
          "pattern": "^[cC][^\\s-]{8,}$"
        },
        "signerName": {
          "type": "string",
          "minLength": 1,
          "maxLength": 200
        },
        "waiverAccepted": {
          "type": "boolean",
          "const": true
        },
        "signatureImage": {
          "type": "string",
          "maxLength": 400000,
          "pattern": "^data:image\\/png;base64,[A-Za-z0-9+/=]+$"
        }
      },
      "required": [
        "responseId",
        "signerName",
        "waiverAccepted"
      ],
      "additionalProperties": false
    }
    Response 201SignedDocumentCreated
    {
      "type": "object",
      "properties": {
        "id": {
          "type": "string"
        },
        "formId": {
          "type": [
            "string",
            "null"
          ]
        },
        "responseId": {
          "type": [
            "string",
            "null"
          ]
        },
        "userId": {
          "type": "string"
        },
        "signerName": {
          "type": "string"
        },
        "signedAt": {
          "type": "string",
          "format": "date-time",
          "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
        },
        "sha256": {
          "type": "string",
          "pattern": "^[0-9a-f]{64}$"
        },
        "pdfUrl": {
          "type": "string"
        },
        "textUrl": {
          "type": "string"
        }
      },
      "required": [
        "id",
        "formId",
        "responseId",
        "userId",
        "signerName",
        "signedAt",
        "sha256",
        "pdfUrl",
        "textUrl"
      ],
      "additionalProperties": false
    }
  • POST/api/forms/parq

    Add the PAR-Q health screen to the coach's forms (one copy per coach)

    Request bodyParqFormCreate
    {
      "type": "object",
      "properties": {
        "assignOnJoin": {
          "type": "boolean"
        }
      },
      "additionalProperties": false
    }
    Response 201ParqFormSummary
    {
      "type": "object",
      "properties": {
        "id": {
          "type": "string"
        },
        "title": {
          "type": "string"
        },
        "assignOnJoin": {
          "type": "boolean"
        }
      },
      "required": [
        "id",
        "title",
        "assignOnJoin"
      ],
      "additionalProperties": false
    }
  • GET/api/signed-documents/{id}

    A signed form response (its signer, or the coach it was signed for)

    Parameters
    NameLocationRequiredDescription
    idpathYes
    formatqueryNo`pdf` for the receipt, `text` for the signed UTF-8 text the SHA-256 is over
    Response 200SignedDocumentView
    {
      "type": "object",
      "properties": {
        "id": {
          "type": "string"
        },
        "formId": {
          "type": [
            "string",
            "null"
          ]
        },
        "responseId": {
          "type": [
            "string",
            "null"
          ]
        },
        "userId": {
          "type": "string"
        },
        "signerName": {
          "type": "string"
        },
        "signedAt": {
          "type": "string",
          "format": "date-time",
          "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
        },
        "sha256": {
          "type": "string",
          "pattern": "^[0-9a-f]{64}$"
        },
        "intact": {
          "type": "boolean"
        },
        "hasDrawnSignature": {
          "type": "boolean"
        },
        "pdfUrl": {
          "type": "string"
        },
        "textUrl": {
          "type": "string"
        }
      },
      "required": [
        "id",
        "formId",
        "responseId",
        "userId",
        "signerName",
        "signedAt",
        "sha256",
        "intact",
        "hasDrawnSignature",
        "pdfUrl",
        "textUrl"
      ],
      "additionalProperties": false
    }

Habits

  • GET/api/habit-entries

    A client's habit check-offs

    Parameters
    NameLocationRequiredDescription
    clientIdqueryNoWhose entries (default: the caller)
    habitIdqueryNoOnly this habit
    datequeryNoOne day, YYYY-MM-DD
    startDatequeryNoFrom this day, YYYY-MM-DD
    endDatequeryNoUp to this day, YYYY-MM-DD
    limitqueryNoRows to return (default 500, at most 2000)
    Response 200HabitEntryListResponse
    {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "clientId": {
            "type": "string"
          },
          "habitId": {
            "type": "string"
          },
          "date": {
            "type": "string",
            "description": "Calendar day, YYYY-MM-DD"
          },
          "completed": {
            "type": "boolean"
          },
          "notes": {
            "type": [
              "string",
              "null"
            ]
          },
          "createdAt": {
            "type": "string",
            "description": "ISO-8601 timestamp"
          },
          "habit": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "category": {
                "type": "string"
              },
              "frequency": {
                "type": "string"
              },
              "assigned": {
                "type": "boolean",
                "description": "False when the coach switched it off for this client"
              }
            },
            "required": [
              "id",
              "name",
              "category",
              "frequency",
              "assigned"
            ],
            "additionalProperties": false
          }
        },
        "required": [
          "id",
          "clientId",
          "habitId",
          "date",
          "completed",
          "notes",
          "createdAt",
          "habit"
        ],
        "additionalProperties": {}
      }
    }
  • GET/api/habits

    A coach's habits with recent entries

    Parameters
    NameLocationRequiredDescription
    trainerIdqueryNoThe coach (required for a client)
    daysqueryNoEntry window in days (1 to 365)
    limitqueryNoRows to return (default 500, at most 2000)
    X-TimezoneheaderNoIANA time zone that decides "today", for example Europe/Skopje
    Response 200HabitListResponse
    {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "trainerId": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "frequency": {
            "type": "string"
          },
          "category": {
            "type": "string"
          },
          "createdAt": {
            "type": "string",
            "description": "ISO-8601 timestamp"
          },
          "updatedAt": {
            "type": "string",
            "description": "ISO-8601 timestamp"
          },
          "entries": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string"
                },
                "clientId": {
                  "type": "string"
                },
                "habitId": {
                  "type": "string"
                },
                "date": {
                  "type": "string",
                  "description": "Calendar day, YYYY-MM-DD"
                },
                "completed": {
                  "type": "boolean"
                },
                "notes": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "createdAt": {
                  "type": "string",
                  "description": "ISO-8601 timestamp"
                }
              },
              "required": [
                "id",
                "clientId",
                "habitId",
                "date",
                "completed",
                "notes",
                "createdAt"
              ],
              "additionalProperties": {}
            },
            "description": "Within `?days=` (default window)"
          },
          "unassignments": {
            "description": "Coach only: clients this habit is switched off for",
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "clientId": {
                  "type": "string"
                }
              },
              "required": [
                "clientId"
              ],
              "additionalProperties": false
            }
          }
        },
        "required": [
          "id",
          "trainerId",
          "name",
          "description",
          "frequency",
          "category",
          "createdAt",
          "updatedAt",
          "entries"
        ],
        "additionalProperties": {}
      }
    }
  • POST/api/habits/packs/{slug}/apply

    Add a habit pack (hydration, sleep, steps, protein, mobility, stress) to clients

    Parameters
    NameLocationRequiredDescription
    slugpathYes
    Request bodyHabitPackApply
    {
      "type": "object",
      "properties": {
        "clientIds": {
          "minItems": 1,
          "maxItems": 200,
          "type": "array",
          "items": {
            "type": "string"
          }
        }
      },
      "required": [
        "clientIds"
      ],
      "additionalProperties": false
    }
    Response 200HabitPackApplyResponse
    {
      "type": "object",
      "properties": {
        "packSlug": {
          "type": "string"
        },
        "clientIds": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "habits": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "packItem": {
                "type": "string"
              },
              "created": {
                "type": "boolean"
              }
            },
            "required": [
              "id",
              "name",
              "packItem",
              "created"
            ],
            "additionalProperties": false
          }
        }
      },
      "required": [
        "packSlug",
        "clientIds",
        "habits"
      ],
      "additionalProperties": false
    }

Metrics

  • GET/api/metric-definitions

    The coach's custom metrics (a client sees their coaches')

    Parameters
    NameLocationRequiredDescription
    includeArchivedqueryNo
    limitqueryNoRows to return (default 100, at most 200)
    cursorqueryNoThe `nextCursor` of the previous page
    Response 200MetricDefinitionPage
    {
      "type": "object",
      "properties": {
        "items": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "trainerId": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "unit": {
                "type": "string"
              },
              "archivedAt": {
                "anyOf": [
                  {
                    "type": "string",
                    "description": "ISO-8601 timestamp"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "createdAt": {
                "type": "string",
                "description": "ISO-8601 timestamp"
              },
              "updatedAt": {
                "type": "string",
                "description": "ISO-8601 timestamp"
              }
            },
            "required": [
              "id",
              "trainerId",
              "name",
              "unit",
              "archivedAt",
              "createdAt",
              "updatedAt"
            ],
            "additionalProperties": false
          }
        },
        "nextCursor": {
          "description": "Pass as `?cursor=` for the next page; null on the last",
          "type": [
            "string",
            "null"
          ]
        }
      },
      "required": [
        "items",
        "nextCursor"
      ],
      "additionalProperties": false
    }
  • GET/api/metrics

    Body metric entries, a page at a time

    Parameters
    NameLocationRequiredDescription
    clientIdqueryNo
    datequeryNo
    metricTypequeryNo
    fromqueryNo
    toqueryNo
    daysqueryNo
    limitqueryNoRows to return (default 30, at most 200)
    cursorqueryNoThe `nextCursor` of the previous page
    Response 200MetricEntryPage
    {
      "type": "object",
      "properties": {
        "items": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "clientId": {
                "type": "string"
              },
              "date": {
                "type": "string",
                "description": "Calendar day, YYYY-MM-DD"
              },
              "metricType": {
                "type": "string"
              },
              "value": {
                "type": "number"
              },
              "unit": {
                "type": "string"
              },
              "notes": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "definitionId": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "createdAt": {
                "type": "string",
                "description": "ISO-8601 timestamp"
              },
              "updatedAt": {
                "type": "string",
                "description": "ISO-8601 timestamp"
              },
              "definition": {
                "anyOf": [
                  {
                    "type": "object",
                    "properties": {
                      "id": {
                        "type": "string"
                      },
                      "name": {
                        "type": "string"
                      },
                      "unit": {
                        "type": "string"
                      },
                      "archivedAt": {
                        "anyOf": [
                          {
                            "type": "string",
                            "description": "ISO-8601 timestamp"
                          },
                          {
                            "type": "null"
                          }
                        ]
                      }
                    },
                    "required": [
                      "id",
                      "name",
                      "unit",
                      "archivedAt"
                    ],
                    "additionalProperties": false
                  },
                  {
                    "type": "null"
                  }
                ]
              }
            },
            "required": [
              "id",
              "clientId",
              "date",
              "metricType",
              "value",
              "unit",
              "notes",
              "definitionId",
              "createdAt",
              "updatedAt",
              "definition"
            ],
            "additionalProperties": {}
          }
        },
        "nextCursor": {
          "description": "Pass as `?cursor=` for the next page; null on the last",
          "type": [
            "string",
            "null"
          ]
        }
      },
      "required": [
        "items",
        "nextCursor"
      ],
      "additionalProperties": false
    }
  • GET/api/metrics/{id}

    One metric entry

    Parameters
    NameLocationRequiredDescription
    idpathYes
    Response 200MetricEntryDetail
    {
      "type": "object",
      "properties": {
        "id": {
          "type": "string"
        },
        "clientId": {
          "type": "string"
        },
        "date": {
          "type": "string",
          "description": "Calendar day, YYYY-MM-DD"
        },
        "metricType": {
          "type": "string"
        },
        "value": {
          "type": "number"
        },
        "unit": {
          "type": "string"
        },
        "notes": {
          "type": [
            "string",
            "null"
          ]
        },
        "definitionId": {
          "type": [
            "string",
            "null"
          ]
        },
        "createdAt": {
          "type": "string",
          "description": "ISO-8601 timestamp"
        },
        "updatedAt": {
          "type": "string",
          "description": "ISO-8601 timestamp"
        },
        "client": {
          "type": "object",
          "properties": {
            "id": {
              "type": "string"
            },
            "name": {
              "type": "string"
            },
            "avatar": {
              "type": [
                "string",
                "null"
              ]
            }
          },
          "required": [
            "id",
            "name",
            "avatar"
          ],
          "additionalProperties": {}
        }
      },
      "required": [
        "id",
        "clientId",
        "date",
        "metricType",
        "value",
        "unit",
        "notes",
        "definitionId",
        "createdAt",
        "updatedAt",
        "client"
      ],
      "additionalProperties": {}
    }

Messages

  • GET/api/messages

    One thread between the caller and another person, a page at a time

    Parameters
    NameLocationRequiredDescription
    userIdqueryYesThe caller
    otherUserIdqueryYesThe other person
    limitqueryNoRows to return (default 50, at most 200)
    cursorqueryNoThe `nextCursor` of the previous page
    Response 200MessagePage
    {
      "type": "object",
      "properties": {
        "items": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "senderId": {
                "type": "string"
              },
              "receiverId": {
                "type": "string"
              },
              "content": {
                "type": "string"
              },
              "messageType": {
                "type": "string",
                "enum": [
                  "text",
                  "image",
                  "video",
                  "file",
                  "encouragement",
                  "audio"
                ]
              },
              "isRead": {
                "type": "boolean"
              },
              "readAt": {
                "anyOf": [
                  {
                    "type": "string",
                    "description": "ISO-8601 timestamp"
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "When the receiver read it (WP-05-03); null while unread, or read before receipts existed"
              },
              "badgeCleared": {
                "type": "boolean",
                "description": "The receiver cleared the unread badge without opening it (mark all read, dismissing the summary, blocking): isRead is true but it was not read. Show a read receipt only for readAt, or isRead without badgeCleared"
              },
              "createdAt": {
                "type": "string",
                "description": "ISO-8601 timestamp"
              },
              "conversationId": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "clientMessageId": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "durationMs": {
                "anyOf": [
                  {
                    "type": "integer",
                    "minimum": -9007199254740991,
                    "maximum": 9007199254740991
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "Voice note (`audio`) only: its length in milliseconds, at most 420000"
              },
              "waveform": {
                "type": "array",
                "items": {
                  "type": "integer",
                  "minimum": -9007199254740991,
                  "maximum": 9007199254740991
                },
                "description": "Voice note only: loudness peaks 0-100, at most 64; empty otherwise"
              }
            },
            "required": [
              "id",
              "senderId",
              "receiverId",
              "content",
              "messageType",
              "isRead",
              "readAt",
              "badgeCleared",
              "createdAt",
              "conversationId",
              "clientMessageId",
              "durationMs",
              "waveform"
            ],
            "additionalProperties": {}
          }
        },
        "nextCursor": {
          "description": "Pass as `?cursor=` for the next page; null on the last",
          "type": [
            "string",
            "null"
          ]
        }
      },
      "required": [
        "items",
        "nextCursor"
      ],
      "additionalProperties": false
    }
  • PATCH/api/messages/{id}

    Mark a message addressed to the caller read; stamps `readAt` once

    Parameters
    NameLocationRequiredDescription
    idpathYes
    Response 200Message
    {
      "type": "object",
      "properties": {
        "id": {
          "type": "string"
        },
        "senderId": {
          "type": "string"
        },
        "receiverId": {
          "type": "string"
        },
        "content": {
          "type": "string"
        },
        "messageType": {
          "type": "string",
          "enum": [
            "text",
            "image",
            "video",
            "file",
            "encouragement",
            "audio"
          ]
        },
        "isRead": {
          "type": "boolean"
        },
        "readAt": {
          "anyOf": [
            {
              "type": "string",
              "description": "ISO-8601 timestamp"
            },
            {
              "type": "null"
            }
          ],
          "description": "When the receiver read it (WP-05-03); null while unread, or read before receipts existed"
        },
        "badgeCleared": {
          "type": "boolean",
          "description": "The receiver cleared the unread badge without opening it (mark all read, dismissing the summary, blocking): isRead is true but it was not read. Show a read receipt only for readAt, or isRead without badgeCleared"
        },
        "createdAt": {
          "type": "string",
          "description": "ISO-8601 timestamp"
        },
        "conversationId": {
          "type": [
            "string",
            "null"
          ]
        },
        "clientMessageId": {
          "type": [
            "string",
            "null"
          ]
        },
        "durationMs": {
          "anyOf": [
            {
              "type": "integer",
              "minimum": -9007199254740991,
              "maximum": 9007199254740991
            },
            {
              "type": "null"
            }
          ],
          "description": "Voice note (`audio`) only: its length in milliseconds, at most 420000"
        },
        "waveform": {
          "type": "array",
          "items": {
            "type": "integer",
            "minimum": -9007199254740991,
            "maximum": 9007199254740991
          },
          "description": "Voice note only: loudness peaks 0-100, at most 64; empty otherwise"
        }
      },
      "required": [
        "id",
        "senderId",
        "receiverId",
        "content",
        "messageType",
        "isRead",
        "readAt",
        "badgeCleared",
        "createdAt",
        "conversationId",
        "clientMessageId",
        "durationMs",
        "waveform"
      ],
      "additionalProperties": {}
    }
  • GET/api/messages/voice-notes

    Whether voice notes are on, and the limits a recorder must keep to

    Response 200VoiceNotesConfig
    {
      "type": "object",
      "properties": {
        "enabled": {
          "type": "boolean",
          "description": "`VOICE_NOTES_ENABLED` on this deployment"
        },
        "maxDurationMs": {
          "type": "integer",
          "minimum": -9007199254740991,
          "maximum": 9007199254740991
        },
        "maxBytes": {
          "type": "integer",
          "minimum": -9007199254740991,
          "maximum": 9007199254740991
        },
        "mimeTypes": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "waveformPoints": {
          "type": "integer",
          "minimum": -9007199254740991,
          "maximum": 9007199254740991
        }
      },
      "required": [
        "enabled",
        "maxDurationMs",
        "maxBytes",
        "mimeTypes",
        "waveformPoints"
      ],
      "additionalProperties": false
    }

Nutrition

  • GET/api/foods/favourites

    The caller's starred foods, newest first, each with the amount last logged

  • POST/api/foods/favourites

    Star a food: an Open Food Facts product or one of the caller's own foods

    Request bodyFoodFavouriteCreate
    {
      "type": "object",
      "properties": {
        "offProductId": {
          "anyOf": [
            {
              "type": "string",
              "pattern": "^[cC][^\\s-]{8,}$"
            },
            {
              "type": "null"
            }
          ]
        },
        "customFoodId": {
          "anyOf": [
            {
              "type": "string",
              "pattern": "^[cC][^\\s-]{8,}$"
            },
            {
              "type": "null"
            }
          ]
        }
      },
      "additionalProperties": false
    }
  • DELETE/api/foods/favourites/{foodRef}

    Un-star a food; `foodRef` is `off:<id>` or `custom:<id>`

    Parameters
    NameLocationRequiredDescription
    foodRefpathYes
  • POST/api/foods/recognise

    Suggest what is in a meal photo, as editable estimates. Needs `ai_processing` consent (403 CONSENT_REQUIRED) and a configured provider (501 otherwise)

    Request bodyFoodRecognise
    {
      "type": "object",
      "properties": {
        "photoUrl": {
          "type": "string",
          "minLength": 1,
          "maxLength": 2048
        },
        "lang": {
          "type": "string",
          "pattern": "^[a-z]{2}$"
        }
      },
      "required": [
        "photoUrl"
      ],
      "additionalProperties": false
    }
  • GET/api/nutrition/compliance

    How closely a client ate to their targets over 7, 14 or 30 days

    Parameters
    NameLocationRequiredDescription
    clientIdqueryNoWhose diary (default: the caller)
    endDatequeryNoYYYY-MM-DD (default: today in X-Timezone)
    daysqueryNo7 (default), 14 or 30
    X-TimezoneheaderNoIANA time zone that decides "today", for example Europe/Skopje
    Response 200NutritionComplianceResponse
    {
      "type": "object",
      "properties": {
        "clientId": {
          "type": "string"
        },
        "endDate": {
          "type": "string",
          "description": "Calendar day, YYYY-MM-DD"
        },
        "days": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "date": {
                "type": "string",
                "description": "Calendar day, YYYY-MM-DD"
              },
              "status": {
                "type": "string",
                "enum": [
                  "on_track",
                  "over",
                  "under",
                  "not_logged"
                ]
              },
              "calories": {
                "type": "number"
              },
              "targetCalories": {
                "type": "number"
              },
              "loggedMeals": {
                "type": "integer",
                "minimum": -9007199254740991,
                "maximum": 9007199254740991
              }
            },
            "required": [
              "date",
              "status",
              "calories",
              "targetCalories",
              "loggedMeals"
            ],
            "additionalProperties": false
          }
        },
        "compliancePct": {
          "type": "integer",
          "minimum": 0,
          "maximum": 100
        },
        "thresholdPct": {
          "type": "number"
        },
        "daysLogged": {
          "type": "integer",
          "minimum": -9007199254740991,
          "maximum": 9007199254740991
        },
        "avgCalories": {
          "type": [
            "number",
            "null"
          ]
        },
        "avgProtein": {
          "type": [
            "number",
            "null"
          ]
        },
        "avgCarbs": {
          "type": [
            "number",
            "null"
          ]
        },
        "avgFat": {
          "type": [
            "number",
            "null"
          ]
        }
      },
      "required": [
        "clientId",
        "endDate",
        "days",
        "compliancePct",
        "thresholdPct",
        "daysLogged",
        "avgCalories",
        "avgProtein",
        "avgCarbs",
        "avgFat"
      ],
      "additionalProperties": false
    }
  • POST/api/nutrition/copy

    Copy a day or one meal of the diary to another date or meal (photos are not copied; notes, food links and micros are)

    Request bodyNutritionCopy
    {
      "type": "object",
      "properties": {
        "clientId": {
          "type": "string"
        },
        "from": {
          "type": "object",
          "properties": {
            "date": {
              "type": "string"
            },
            "mealType": {
              "type": "string",
              "enum": [
                "breakfast",
                "lunch",
                "dinner",
                "snack"
              ]
            }
          },
          "required": [
            "date"
          ],
          "additionalProperties": false
        },
        "to": {
          "type": "object",
          "properties": {
            "date": {
              "type": "string"
            },
            "mealType": {
              "type": "string",
              "enum": [
                "breakfast",
                "lunch",
                "dinner",
                "snack"
              ]
            }
          },
          "required": [
            "date"
          ],
          "additionalProperties": false
        },
        "mode": {
          "type": "string",
          "enum": [
            "append",
            "replace"
          ]
        }
      },
      "required": [
        "from",
        "to"
      ],
      "additionalProperties": false
    }
  • POST/api/nutrition/from-plan

    Log a meal of a meal plan the client follows on that date, with its linked food's extra nutrients and micros

    Request bodyNutritionFromPlan
    {
      "type": "object",
      "properties": {
        "clientId": {
          "type": "string"
        },
        "planId": {
          "type": "string",
          "pattern": "^[cC][^\\s-]{8,}$"
        },
        "mealPlanMealId": {
          "type": "string",
          "pattern": "^[cC][^\\s-]{8,}$"
        },
        "date": {
          "type": "string"
        },
        "servings": {
          "type": "number",
          "minimum": 0.25,
          "maximum": 50
        },
        "loggedAt": {
          "anyOf": [
            {
              "type": "string",
              "format": "date-time",
              "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
            },
            {
              "type": "null"
            }
          ]
        },
        "force": {
          "type": "boolean"
        }
      },
      "required": [
        "planId",
        "mealPlanMealId",
        "date"
      ],
      "additionalProperties": false
    }
  • GET/api/nutrition/report

    A client's weekly nutrition report

    Parameters
    NameLocationRequiredDescription
    clientIdqueryNoWhose report (default: the caller)
    weekEndingqueryNoYYYY-MM-DD, the last day of the week (default: yesterday in X-Timezone)
    X-TimezoneheaderNoIANA time zone that decides "today", for example Europe/Skopje
    Response 200NutritionWeeklyReportResponse
    {
      "type": "object",
      "properties": {
        "clientId": {
          "type": "string"
        },
        "weekStart": {
          "type": "string",
          "description": "Calendar day, YYYY-MM-DD"
        },
        "weekEnd": {
          "type": "string",
          "description": "Calendar day, YYYY-MM-DD"
        },
        "daysLogged": {
          "type": "integer",
          "minimum": -9007199254740991,
          "maximum": 9007199254740991
        },
        "compliancePct": {
          "type": "integer",
          "minimum": -9007199254740991,
          "maximum": 9007199254740991
        },
        "averages": {
          "type": "object",
          "properties": {
            "calories": {
              "type": [
                "number",
                "null"
              ]
            },
            "protein": {
              "type": [
                "number",
                "null"
              ]
            },
            "carbs": {
              "type": [
                "number",
                "null"
              ]
            },
            "fat": {
              "type": [
                "number",
                "null"
              ]
            },
            "fiber": {
              "type": [
                "number",
                "null"
              ]
            },
            "sugar": {
              "type": [
                "number",
                "null"
              ]
            },
            "saturatedFat": {
              "type": [
                "number",
                "null"
              ]
            },
            "sodiumMg": {
              "type": [
                "number",
                "null"
              ]
            },
            "waterMl": {
              "type": [
                "number",
                "null"
              ]
            }
          },
          "required": [
            "calories",
            "protein",
            "carbs",
            "fat",
            "fiber",
            "sugar",
            "saturatedFat",
            "sodiumMg",
            "waterMl"
          ],
          "additionalProperties": false
        },
        "targets": {
          "type": "object",
          "properties": {
            "calories": {
              "type": "number"
            },
            "protein": {
              "type": "number"
            },
            "carbs": {
              "type": "number"
            },
            "fat": {
              "type": "number"
            }
          },
          "required": [
            "calories",
            "protein",
            "carbs",
            "fat"
          ],
          "additionalProperties": false
        },
        "bestDay": {
          "anyOf": [
            {
              "type": "object",
              "properties": {
                "date": {
                  "type": "string",
                  "description": "Calendar day, YYYY-MM-DD"
                },
                "calories": {
                  "type": "number"
                }
              },
              "required": [
                "date",
                "calories"
              ],
              "additionalProperties": false
            },
            {
              "type": "null"
            }
          ]
        },
        "worstDay": {
          "anyOf": [
            {
              "type": "object",
              "properties": {
                "date": {
                  "type": "string",
                  "description": "Calendar day, YYYY-MM-DD"
                },
                "calories": {
                  "type": "number"
                }
              },
              "required": [
                "date",
                "calories"
              ],
              "additionalProperties": false
            },
            {
              "type": "null"
            }
          ]
        },
        "topFoods": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "name": {
                "type": "string"
              },
              "count": {
                "type": "integer",
                "minimum": -9007199254740991,
                "maximum": 9007199254740991
              },
              "calories": {
                "type": "number"
              },
              "source": {
                "type": "string"
              }
            },
            "required": [
              "name",
              "count",
              "calories",
              "source"
            ],
            "additionalProperties": false
          }
        },
        "photosLogged": {
          "type": "integer",
          "minimum": -9007199254740991,
          "maximum": 9007199254740991
        },
        "comments": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "body": {
                "type": "string"
              },
              "createdAt": {
                "type": "string",
                "description": "ISO-8601 timestamp"
              }
            },
            "required": [
              "id",
              "body",
              "createdAt"
            ],
            "additionalProperties": {}
          }
        },
        "partialNutrients": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "attribution": {
          "anyOf": [
            {
              "type": "object",
              "properties": {
                "text": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                }
              },
              "required": [
                "text",
                "url"
              ],
              "additionalProperties": {}
            },
            {
              "type": "null"
            }
          ]
        }
      },
      "required": [
        "clientId",
        "weekStart",
        "weekEnd",
        "daysLogged",
        "compliancePct",
        "averages",
        "targets",
        "bestDay",
        "worstDay",
        "topFoods",
        "photosLogged",
        "comments",
        "partialNutrients",
        "attribution"
      ],
      "additionalProperties": false
    }
  • GET/api/nutrition/summary

    A day's nutrition totals against the client's goals

    Parameters
    NameLocationRequiredDescription
    clientIdqueryNoWhose day (default: the caller)
    datequeryNoYYYY-MM-DD (default: today in X-Timezone)
    X-TimezoneheaderNoIANA time zone that decides "today", for example Europe/Skopje
    Response 200NutritionDaySummaryResponse
    {
      "type": "object",
      "properties": {
        "clientId": {
          "type": "string"
        },
        "date": {
          "type": "string",
          "description": "Calendar day, YYYY-MM-DD"
        },
        "calories": {
          "type": "number"
        },
        "protein": {
          "type": "number"
        },
        "carbs": {
          "type": "number"
        },
        "fat": {
          "type": "number"
        },
        "fiber": {
          "type": "number"
        },
        "entryCount": {
          "type": "integer",
          "minimum": -9007199254740991,
          "maximum": 9007199254740991
        },
        "goals": {
          "type": "object",
          "properties": {
            "calories": {
              "type": "number"
            },
            "protein": {
              "type": "number"
            },
            "carbs": {
              "type": "number"
            },
            "fat": {
              "type": "number"
            }
          },
          "required": [
            "calories",
            "protein",
            "carbs",
            "fat"
          ],
          "additionalProperties": {}
        },
        "targetSource": {
          "type": "string",
          "enum": [
            "override",
            "dayType",
            "set",
            "onboarding",
            "default"
          ],
          "description": "Where the goals come from; treat an unknown value as \"set\""
        },
        "dayType": {
          "anyOf": [
            {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string"
                },
                "name": {
                  "type": "string"
                }
              },
              "required": [
                "id",
                "name"
              ],
              "additionalProperties": false
            },
            {
              "type": "null"
            }
          ]
        },
        "meals": {
          "type": "object",
          "properties": {
            "breakfast": {
              "type": "object",
              "properties": {
                "calories": {
                  "type": "number"
                },
                "protein": {
                  "type": "number"
                },
                "carbs": {
                  "type": "number"
                },
                "fat": {
                  "type": "number"
                },
                "fiber": {
                  "type": "number"
                },
                "sugar": {
                  "type": "number"
                },
                "saturatedFat": {
                  "type": "number"
                },
                "sodiumMg": {
                  "type": "number"
                },
                "entryCount": {
                  "type": "integer",
                  "minimum": -9007199254740991,
                  "maximum": 9007199254740991
                },
                "partial": {
                  "type": "boolean",
                  "description": "An entry in the meal leaves sugar, saturated fat or sodium unknown"
                }
              },
              "required": [
                "calories",
                "protein",
                "carbs",
                "fat",
                "fiber",
                "sugar",
                "saturatedFat",
                "sodiumMg",
                "entryCount",
                "partial"
              ],
              "additionalProperties": false
            },
            "lunch": {
              "type": "object",
              "properties": {
                "calories": {
                  "type": "number"
                },
                "protein": {
                  "type": "number"
                },
                "carbs": {
                  "type": "number"
                },
                "fat": {
                  "type": "number"
                },
                "fiber": {
                  "type": "number"
                },
                "sugar": {
                  "type": "number"
                },
                "saturatedFat": {
                  "type": "number"
                },
                "sodiumMg": {
                  "type": "number"
                },
                "entryCount": {
                  "type": "integer",
                  "minimum": -9007199254740991,
                  "maximum": 9007199254740991
                },
                "partial": {
                  "type": "boolean",
                  "description": "An entry in the meal leaves sugar, saturated fat or sodium unknown"
                }
              },
              "required": [
                "calories",
                "protein",
                "carbs",
                "fat",
                "fiber",
                "sugar",
                "saturatedFat",
                "sodiumMg",
                "entryCount",
                "partial"
              ],
              "additionalProperties": false
            },
            "dinner": {
              "type": "object",
              "properties": {
                "calories": {
                  "type": "number"
                },
                "protein": {
                  "type": "number"
                },
                "carbs": {
                  "type": "number"
                },
                "fat": {
                  "type": "number"
                },
                "fiber": {
                  "type": "number"
                },
                "sugar": {
                  "type": "number"
                },
                "saturatedFat": {
                  "type": "number"
                },
                "sodiumMg": {
                  "type": "number"
                },
                "entryCount": {
                  "type": "integer",
                  "minimum": -9007199254740991,
                  "maximum": 9007199254740991
                },
                "partial": {
                  "type": "boolean",
                  "description": "An entry in the meal leaves sugar, saturated fat or sodium unknown"
                }
              },
              "required": [
                "calories",
                "protein",
                "carbs",
                "fat",
                "fiber",
                "sugar",
                "saturatedFat",
                "sodiumMg",
                "entryCount",
                "partial"
              ],
              "additionalProperties": false
            },
            "snack": {
              "type": "object",
              "properties": {
                "calories": {
                  "type": "number"
                },
                "protein": {
                  "type": "number"
                },
                "carbs": {
                  "type": "number"
                },
                "fat": {
                  "type": "number"
                },
                "fiber": {
                  "type": "number"
                },
                "sugar": {
                  "type": "number"
                },
                "saturatedFat": {
                  "type": "number"
                },
                "sodiumMg": {
                  "type": "number"
                },
                "entryCount": {
                  "type": "integer",
                  "minimum": -9007199254740991,
                  "maximum": 9007199254740991
                },
                "partial": {
                  "type": "boolean",
                  "description": "An entry in the meal leaves sugar, saturated fat or sodium unknown"
                }
              },
              "required": [
                "calories",
                "protein",
                "carbs",
                "fat",
                "fiber",
                "sugar",
                "saturatedFat",
                "sodiumMg",
                "entryCount",
                "partial"
              ],
              "additionalProperties": false
            }
          },
          "required": [
            "breakfast",
            "lunch",
            "dinner",
            "snack"
          ],
          "additionalProperties": false
        },
        "extras": {
          "type": "object",
          "properties": {
            "sugar": {
              "type": "number"
            },
            "saturatedFat": {
              "type": "number"
            },
            "sodiumMg": {
              "type": "number"
            },
            "partialNutrients": {
              "type": "array",
              "items": {
                "type": "string",
                "enum": [
                  "sugar",
                  "sodium",
                  "saturatedFat"
                ]
              }
            }
          },
          "required": [
            "sugar",
            "saturatedFat",
            "sodiumMg",
            "partialNutrients"
          ],
          "additionalProperties": false
        },
        "remaining": {
          "type": "object",
          "properties": {
            "calories": {
              "type": "number"
            },
            "protein": {
              "type": "number"
            },
            "carbs": {
              "type": "number"
            },
            "fat": {
              "type": "number"
            }
          },
          "required": [
            "calories",
            "protein",
            "carbs",
            "fat"
          ],
          "additionalProperties": false,
          "description": "Goal minus eaten; negative when over"
        },
        "water": {
          "type": "object",
          "properties": {
            "ml": {
              "type": "number"
            },
            "goalMl": {
              "type": "number"
            }
          },
          "required": [
            "ml",
            "goalMl"
          ],
          "additionalProperties": false
        }
      },
      "required": [
        "clientId",
        "date",
        "calories",
        "protein",
        "carbs",
        "fat",
        "fiber",
        "entryCount",
        "goals",
        "targetSource",
        "dayType",
        "meals",
        "extras",
        "remaining",
        "water"
      ],
      "additionalProperties": false
    }
  • PUT/api/nutrition/targets/overrides

    Use one day type, or one calorie figure, on one date only

    Parameters
    NameLocationRequiredDescription
    clientIdqueryNoWhose target (default: the caller); a coach's linked client
    Request bodyNutritionTargetOverride
    {
      "type": "object",
      "properties": {
        "date": {
          "type": "string"
        },
        "dayTypeId": {
          "anyOf": [
            {
              "type": "string",
              "pattern": "^[cC][^\\s-]{8,}$"
            },
            {
              "type": "null"
            }
          ]
        },
        "calories": {
          "anyOf": [
            {
              "type": "integer",
              "minimum": 500,
              "maximum": 10000
            },
            {
              "type": "null"
            }
          ]
        }
      },
      "required": [
        "date",
        "dayTypeId"
      ],
      "additionalProperties": false
    }
  • DELETE/api/nutrition/targets/overrides

    Remove a one-date override: the date follows the week again

    Parameters
    NameLocationRequiredDescription
    clientIdqueryNo
    datequeryYes
  • POST/api/recipes/{id}/log

    Log servings of a recipe the client may use, with the items' summed micros

    Parameters
    NameLocationRequiredDescription
    idpathYes
    Request bodyRecipeLog
    {
      "type": "object",
      "properties": {
        "clientId": {
          "type": "string"
        },
        "date": {
          "type": "string"
        },
        "mealType": {
          "type": "string",
          "enum": [
            "breakfast",
            "lunch",
            "dinner",
            "snack"
          ]
        },
        "servings": {
          "type": "number",
          "minimum": 0.25,
          "maximum": 50
        },
        "loggedAt": {
          "anyOf": [
            {
              "type": "string",
              "format": "date-time",
              "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
            },
            {
              "type": "null"
            }
          ]
        },
        "photoUrl": {
          "anyOf": [
            {
              "type": "string",
              "minLength": 1,
              "maxLength": 2048
            },
            {
              "type": "null"
            }
          ]
        }
      },
      "required": [
        "date",
        "mealType"
      ],
      "additionalProperties": false
    }
  • POST/api/recipes/{id}/save-as-food

    Save one serving of a recipe as one of the caller's own foods

    Parameters
    NameLocationRequiredDescription
    idpathYes
  • GET/api/users/me/dietary

    The caller's allergens and diets, which the food screens flag

  • PUT/api/users/me/dietary

    Replace the caller's allergens and diets (from the fixed lists)

    Request bodyDietaryProfile
    {
      "type": "object",
      "properties": {
        "allergens": {
          "maxItems": 14,
          "type": "array",
          "items": {
            "type": "string",
            "enum": [
              "gluten",
              "milk",
              "eggs",
              "nuts",
              "peanuts",
              "sesame-seeds",
              "soybeans",
              "fish",
              "crustaceans",
              "molluscs",
              "celery",
              "mustard",
              "lupin",
              "sulphur-dioxide-and-sulphites"
            ]
          }
        },
        "diet": {
          "maxItems": 9,
          "type": "array",
          "items": {
            "type": "string",
            "enum": [
              "vegetarian",
              "vegan",
              "pescatarian",
              "halal",
              "kosher",
              "gluten_free",
              "lactose_free",
              "low_carb",
              "keto"
            ]
          }
        }
      },
      "required": [
        "allergens",
        "diet"
      ],
      "additionalProperties": false
    }
  • POST/api/water

    Set or add to a day's water: exactly one of `glasses`, `ml` (the day's total) or `addMl` (added in one atomic step; negative takes back)

    Request bodyWaterUpsert
    {
      "type": "object",
      "properties": {
        "clientId": {
          "type": "string"
        },
        "date": {
          "type": "string"
        },
        "glasses": {
          "type": "integer",
          "minimum": 0,
          "maximum": 32
        },
        "ml": {
          "type": "integer",
          "minimum": 0,
          "maximum": 20000
        },
        "addMl": {
          "type": "integer",
          "minimum": -5000,
          "maximum": 5000
        }
      },
      "required": [
        "date"
      ],
      "additionalProperties": false
    }

Comments

  • GET/api/log-comments/counts

    Comments per thread for one target type on one day: how many, and how many the caller has not read

    Parameters
    NameLocationRequiredDescription
    targetTypequeryYesWhat the threads hang off, for example `nutrition_entry`
    datequeryYesYYYY-MM-DD
    clientIdqueryNoA coach's linked client (default: every active client); ignored for a client

Exports

  • GET/api/exports/coach

    The coach's recent whole-practice exports, with download links for ready parts. Start one with POST (second factor required, not part of this reference)

    Response 200CoachExportJobList
    {
      "type": "object",
      "properties": {
        "items": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "status": {
                "type": "string",
                "enum": [
                  "queued",
                  "running",
                  "ready",
                  "failed"
                ]
              },
              "createdAt": {
                "type": "string",
                "description": "ISO-8601 timestamp"
              },
              "finishedAt": {
                "anyOf": [
                  {
                    "type": "string",
                    "description": "ISO-8601 timestamp"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "expiresAt": {
                "anyOf": [
                  {
                    "type": "string",
                    "description": "ISO-8601 timestamp"
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "After this the parts are no longer offered"
              },
              "parts": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "part": {
                      "type": "integer",
                      "minimum": -9007199254740991,
                      "maximum": 9007199254740991,
                      "description": "1-based part number"
                    },
                    "bytes": {
                      "type": "integer",
                      "minimum": -9007199254740991,
                      "maximum": 9007199254740991
                    },
                    "files": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "File names in this ZIP, besides manifest.json"
                    },
                    "url": {
                      "description": "15-minute download link; null unless ready",
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  },
                  "required": [
                    "part",
                    "bytes",
                    "files",
                    "url"
                  ],
                  "additionalProperties": {}
                }
              }
            },
            "required": [
              "id",
              "status",
              "createdAt",
              "finishedAt",
              "expiresAt",
              "parts"
            ],
            "additionalProperties": {}
          },
          "description": "The five most recent exports, newest first"
        },
        "nextCursor": {
          "type": "null"
        }
      },
      "required": [
        "items",
        "nextCursor"
      ],
      "additionalProperties": false
    }
  • GET/api/exports/coach/{id}

    One of the caller's whole-practice exports

    Parameters
    NameLocationRequiredDescription
    idpathYes
    Response 200CoachExportJob
    {
      "type": "object",
      "properties": {
        "id": {
          "type": "string"
        },
        "status": {
          "type": "string",
          "enum": [
            "queued",
            "running",
            "ready",
            "failed"
          ]
        },
        "createdAt": {
          "type": "string",
          "description": "ISO-8601 timestamp"
        },
        "finishedAt": {
          "anyOf": [
            {
              "type": "string",
              "description": "ISO-8601 timestamp"
            },
            {
              "type": "null"
            }
          ]
        },
        "expiresAt": {
          "anyOf": [
            {
              "type": "string",
              "description": "ISO-8601 timestamp"
            },
            {
              "type": "null"
            }
          ],
          "description": "After this the parts are no longer offered"
        },
        "parts": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "part": {
                "type": "integer",
                "minimum": -9007199254740991,
                "maximum": 9007199254740991,
                "description": "1-based part number"
              },
              "bytes": {
                "type": "integer",
                "minimum": -9007199254740991,
                "maximum": 9007199254740991
              },
              "files": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "File names in this ZIP, besides manifest.json"
              },
              "url": {
                "description": "15-minute download link; null unless ready",
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "required": [
              "part",
              "bytes",
              "files",
              "url"
            ],
            "additionalProperties": {}
          }
        }
      },
      "required": [
        "id",
        "status",
        "createdAt",
        "finishedAt",
        "expiresAt",
        "parts"
      ],
      "additionalProperties": {}
    }

Studios

  • GET/api/tenants/{id}/audit

    The studio's audit log, for its owner only

    Parameters
    NameLocationRequiredDescription
    idpathYes
    actorIdqueryNo
    actionqueryNo
    fromqueryNo
    toqueryNo
    cursorqueryNo
    limitqueryNo
    Response 200StudioAuditPage
    {
      "type": "object",
      "properties": {
        "items": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "action": {
                "type": "string"
              },
              "actor": {
                "anyOf": [
                  {
                    "type": "object",
                    "properties": {
                      "id": {
                        "type": "string"
                      },
                      "name": {
                        "type": "string"
                      }
                    },
                    "required": [
                      "id",
                      "name"
                    ],
                    "additionalProperties": false
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "targetType": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "targetId": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "metadata": {},
              "createdAt": {
                "type": "string",
                "description": "ISO-8601 timestamp"
              }
            },
            "required": [
              "id",
              "action",
              "actor",
              "targetType",
              "targetId",
              "metadata",
              "createdAt"
            ],
            "additionalProperties": {}
          }
        },
        "nextCursor": {
          "description": "Pass as `?cursor=` for the next page",
          "type": [
            "string",
            "null"
          ]
        },
        "staff": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              }
            },
            "required": [
              "id",
              "name"
            ],
            "additionalProperties": false
          },
          "description": "The studio's coaches, for the actor filter"
        }
      },
      "required": [
        "items",
        "nextCursor",
        "staff"
      ],
      "additionalProperties": false
    }

Feedback

  • GET/api/nps

    Whether the NPS prompt is on and the caller is due it

    Response 200NpsState
    {
      "type": "object",
      "properties": {
        "enabled": {
          "type": "boolean",
          "description": "NPS_ENABLED is on"
        },
        "eligible": {
          "type": "boolean",
          "description": "The caller is due the prompt now"
        }
      },
      "required": [
        "enabled",
        "eligible"
      ],
      "additionalProperties": false
    }
  • POST/api/nps

    Answer the NPS prompt (0-10 and an optional comment) or dismiss it

    Request bodyNpsSubmit
    {
      "anyOf": [
        {
          "type": "object",
          "properties": {
            "score": {
              "type": "integer",
              "minimum": 0,
              "maximum": 10
            },
            "comment": {
              "type": "string",
              "maxLength": 1000
            }
          },
          "required": [
            "score"
          ],
          "additionalProperties": false
        },
        {
          "type": "object",
          "properties": {
            "dismissed": {
              "type": "boolean",
              "const": true
            }
          },
          "required": [
            "dismissed"
          ],
          "additionalProperties": false
        }
      ]
    }
    Response 201NpsResponseCreated
    {
      "type": "object",
      "properties": {
        "id": {
          "type": "string"
        },
        "score": {
          "anyOf": [
            {
              "type": "integer",
              "minimum": -9007199254740991,
              "maximum": 9007199254740991
            },
            {
              "type": "null"
            }
          ],
          "description": "null for a dismissal"
        },
        "createdAt": {
          "type": "string",
          "description": "ISO-8601 timestamp"
        }
      },
      "required": [
        "id",
        "score",
        "createdAt"
      ],
      "additionalProperties": {}
    }

Referrals

  • GET/api/referrals

    The coach's referral code, link and ledger totals

    Response 200ReferralSummary
    {
      "type": "object",
      "properties": {
        "code": {
          "type": "string",
          "description": "The coach referral code, 8 characters"
        },
        "link": {
          "type": "string",
          "description": "Sign-up link carrying the code"
        },
        "referred": {
          "type": "integer",
          "minimum": -9007199254740991,
          "maximum": 9007199254740991,
          "description": "Coaches who signed up with it"
        },
        "rewards": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "amountMinor": {
                "type": "integer",
                "minimum": -9007199254740991,
                "maximum": 9007199254740991
              },
              "currency": {
                "type": "string"
              }
            },
            "required": [
              "amountMinor",
              "currency"
            ],
            "additionalProperties": false
          },
          "description": "Ledger totals per currency; 0 unless a reward is configured"
        }
      },
      "required": [
        "code",
        "link",
        "referred",
        "rewards"
      ],
      "additionalProperties": false
    }

Webhooks

Add an endpoint under Settings. NetFit sends a signed POST request with a JSON body when one of these events happens. Payloads carry ids and timestamps only: read the details through the API.

Events

  • workout.completedWorkout completed

    Payload
    {
      "type": "object",
      "properties": {
        "id": {
          "type": "string",
          "description": "Delivery id; the same as the NetFit-Delivery header"
        },
        "event": {
          "type": "string",
          "const": "workout.completed"
        },
        "createdAt": {
          "type": "string",
          "description": "ISO-8601 time the event was queued"
        },
        "data": {
          "type": "object",
          "properties": {
            "assignedWorkoutId": {
              "type": "string"
            },
            "workoutLogId": {
              "description": "null when the session was marked done without logging sets",
              "type": [
                "string",
                "null"
              ]
            },
            "clientId": {
              "type": "string"
            },
            "completedAt": {
              "type": "string"
            }
          },
          "required": [
            "assignedWorkoutId",
            "workoutLogId",
            "clientId",
            "completedAt"
          ],
          "additionalProperties": false
        }
      },
      "required": [
        "id",
        "event",
        "createdAt",
        "data"
      ],
      "additionalProperties": false
    }
  • checkin.submittedCheck-in submitted

    Payload
    {
      "type": "object",
      "properties": {
        "id": {
          "type": "string",
          "description": "Delivery id; the same as the NetFit-Delivery header"
        },
        "event": {
          "type": "string",
          "const": "checkin.submitted"
        },
        "createdAt": {
          "type": "string",
          "description": "ISO-8601 time the event was queued"
        },
        "data": {
          "type": "object",
          "properties": {
            "responseId": {
              "type": "string"
            },
            "formId": {
              "type": "string"
            },
            "clientId": {
              "type": "string"
            },
            "submittedAt": {
              "type": "string"
            }
          },
          "required": [
            "responseId",
            "formId",
            "clientId",
            "submittedAt"
          ],
          "additionalProperties": false
        }
      },
      "required": [
        "id",
        "event",
        "createdAt",
        "data"
      ],
      "additionalProperties": false
    }
  • booking.createdSession booked

    Payload
    {
      "type": "object",
      "properties": {
        "id": {
          "type": "string",
          "description": "Delivery id; the same as the NetFit-Delivery header"
        },
        "event": {
          "type": "string",
          "const": "booking.created"
        },
        "createdAt": {
          "type": "string",
          "description": "ISO-8601 time the event was queued"
        },
        "data": {
          "type": "object",
          "properties": {
            "bookingId": {
              "type": "string"
            },
            "clientId": {
              "type": [
                "string",
                "null"
              ]
            },
            "startsAt": {
              "type": "string"
            }
          },
          "required": [
            "bookingId",
            "clientId",
            "startsAt"
          ],
          "additionalProperties": false
        }
      },
      "required": [
        "id",
        "event",
        "createdAt",
        "data"
      ],
      "additionalProperties": false
    }
  • booking.cancelledSession cancelled

    Payload
    {
      "type": "object",
      "properties": {
        "id": {
          "type": "string",
          "description": "Delivery id; the same as the NetFit-Delivery header"
        },
        "event": {
          "type": "string",
          "const": "booking.cancelled"
        },
        "createdAt": {
          "type": "string",
          "description": "ISO-8601 time the event was queued"
        },
        "data": {
          "type": "object",
          "properties": {
            "bookingId": {
              "type": "string"
            },
            "clientId": {
              "type": [
                "string",
                "null"
              ]
            },
            "startsAt": {
              "type": "string"
            }
          },
          "required": [
            "bookingId",
            "clientId",
            "startsAt"
          ],
          "additionalProperties": false
        }
      },
      "required": [
        "id",
        "event",
        "createdAt",
        "data"
      ],
      "additionalProperties": false
    }
  • client.joinedClient joined

    Payload
    {
      "type": "object",
      "properties": {
        "id": {
          "type": "string",
          "description": "Delivery id; the same as the NetFit-Delivery header"
        },
        "event": {
          "type": "string",
          "const": "client.joined"
        },
        "createdAt": {
          "type": "string",
          "description": "ISO-8601 time the event was queued"
        },
        "data": {
          "type": "object",
          "properties": {
            "clientId": {
              "type": "string"
            },
            "joinedAt": {
              "type": "string"
            }
          },
          "required": [
            "clientId",
            "joinedAt"
          ],
          "additionalProperties": false
        }
      },
      "required": [
        "id",
        "event",
        "createdAt",
        "data"
      ],
      "additionalProperties": false
    }
  • pingTest

    Payload
    {
      "type": "object",
      "properties": {
        "id": {
          "type": "string",
          "description": "Delivery id; the same as the NetFit-Delivery header"
        },
        "event": {
          "type": "string",
          "const": "ping"
        },
        "createdAt": {
          "type": "string",
          "description": "ISO-8601 time the event was queued"
        },
        "data": {
          "type": "object",
          "properties": {
            "endpointId": {
              "type": "string"
            },
            "sentAt": {
              "type": "string"
            }
          },
          "required": [
            "endpointId",
            "sentAt"
          ],
          "additionalProperties": false
        }
      },
      "required": [
        "id",
        "event",
        "createdAt",
        "data"
      ],
      "additionalProperties": false
    }

Verify each delivery

  1. Read the NetFit-Signature header. It has the form t=TIMESTAMP,v1=SIGNATURE, with TIMESTAMP in Unix seconds.
  2. Compute the HMAC-SHA256 of TIMESTAMP.BODY (the timestamp, a full stop, then the raw body) with the endpoint's secret, before you parse the body.
  3. Compare the hex result with SIGNATURE in constant time, and reject a TIMESTAMP more than five minutes away from your clock.
  4. Answer with a 2xx status within 10 seconds. Anything else is retried after 1 min, 5 min, 30 min and 2 h. The NetFit-Delivery header stays the same on every retry, so use it to skip duplicates.

Secret rotation

After an endpoint’s secret is rotated, each request carries two v1 signatures for 24 hours, one per secret. Accept the request when any v1 matches.

Failures, replays and switch-off

A delivery that still fails after five attempts is marked failed. After 10 failed deliveries in a row the endpoint is switched off and its owner is notified; switching it back on sends the queued events. Any finished delivery can be resent from the delivery log, and the copy has a new NetFit-Delivery id.

Example in Node.js

import { createHmac, timingSafeEqual } from 'node:crypto'

// rawBody: the request body exactly as received, before JSON.parse.
export function verifyNetFitWebhook(rawBody, header, secret, toleranceSeconds = 300) {
  let timestamp = NaN
  const signatures = []
  for (const part of (header ?? '').split(',')) {
    const [key, value] = part.trim().split('=')
    if (key === 't') timestamp = Number(value)
    if (key === 'v1' && value) signatures.push(value)
  }
  if (!Number.isInteger(timestamp) || signatures.length === 0) return false
  if (Math.abs(Date.now() / 1000 - timestamp) > toleranceSeconds) return false
  const expected = createHmac('sha256', secret).update(`${timestamp}.${rawBody}`).digest()
  // Any v1 may match: after a rotation there is one per secret.
  return signatures.some((signature) => {
    const given = Buffer.from(signature, 'hex')
    return given.length === expected.length && timingSafeEqual(given, expected)
  })
}