curl --request GET \
--url https://app.skcal.fit/api/workouts \
--cookie __Secure-better-auth.session_token=import requests
url = "https://app.skcal.fit/api/workouts"
headers = {"cookie": "__Secure-better-auth.session_token="}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {cookie: '__Secure-better-auth.session_token='}};
fetch('https://app.skcal.fit/api/workouts', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://app.skcal.fit/api/workouts",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_COOKIE => "__Secure-better-auth.session_token=",
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://app.skcal.fit/api/workouts"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("cookie", "__Secure-better-auth.session_token=")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://app.skcal.fit/api/workouts")
.header("cookie", "__Secure-better-auth.session_token=")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.skcal.fit/api/workouts")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["cookie"] = '__Secure-better-auth.session_token='
response = http.request(request)
puts response.read_body[
{
"id": "9c2d1e4f-7a8b-4c5d-9e0f-1a2b3c4d5e6f",
"source": "manual",
"activityType": "run",
"summary": "5k run + pullups",
"description": "ran 5k easy, then 3x10 pullups",
"startedAt": 1783100000000,
"durationS": 1680,
"movingDurationS": null,
"distanceM": 5000,
"elevationGainM": null,
"energyKcal": 320,
"avgHr": 148,
"maxHr": null,
"avgPowerW": null,
"avgCadence": null,
"exercises": [
{
"exercise": "pullups",
"sets": 3,
"reps": 10,
"weightLb": null,
"durationS": null,
"distanceM": null,
"notes": null
}
],
"createdAt": 1783100001000
}
]{
"error": "weightKg must be 9-320 kg",
"detail": "upstream model returned 500"
}List a day's workouts
Returns the caller’s workouts whose start time falls on the given local day, newest first.
curl --request GET \
--url https://app.skcal.fit/api/workouts \
--cookie __Secure-better-auth.session_token=import requests
url = "https://app.skcal.fit/api/workouts"
headers = {"cookie": "__Secure-better-auth.session_token="}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {cookie: '__Secure-better-auth.session_token='}};
fetch('https://app.skcal.fit/api/workouts', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://app.skcal.fit/api/workouts",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_COOKIE => "__Secure-better-auth.session_token=",
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://app.skcal.fit/api/workouts"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("cookie", "__Secure-better-auth.session_token=")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://app.skcal.fit/api/workouts")
.header("cookie", "__Secure-better-auth.session_token=")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.skcal.fit/api/workouts")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["cookie"] = '__Secure-better-auth.session_token='
response = http.request(request)
puts response.read_body[
{
"id": "9c2d1e4f-7a8b-4c5d-9e0f-1a2b3c4d5e6f",
"source": "manual",
"activityType": "run",
"summary": "5k run + pullups",
"description": "ran 5k easy, then 3x10 pullups",
"startedAt": 1783100000000,
"durationS": 1680,
"movingDurationS": null,
"distanceM": 5000,
"elevationGainM": null,
"energyKcal": 320,
"avgHr": 148,
"maxHr": null,
"avgPowerW": null,
"avgCadence": null,
"exercises": [
{
"exercise": "pullups",
"sets": 3,
"reps": 10,
"weightLb": null,
"durationS": null,
"distanceM": null,
"notes": null
}
],
"createdAt": 1783100001000
}
]{
"error": "weightKg must be 9-320 kg",
"detail": "upstream model returned 500"
}Authorizations
Better Auth session. Signing in (Google or email magic link) sets an HttpOnly session cookie that scopes every request to that account. The web app and CLI use this; agents use the OAuth 2.1 flow of the MCP server.
Query Parameters
Local day (YYYY-MM-DD). Defaults to today.
Client timezone offset in minutes (getTimezoneOffset) used to bound the local day.
Response
The day's workouts.
Workout identifier.
"9c2d1e4f-7a8b-4c5d-9e0f-1a2b3c4d5e6f"
Where the workout came from; manual for described/photographed logs.
manual, apple_health, garmin, strava "manual"
Normalized activity type (run, ride, swim, walk, hike, strength_training, yoga, rowing, elliptical, crossfit, other).
"run"
Short AI-generated title.
"5k run + pullups"
The user's original words (or the summary for photo-only logs).
"ran 5k easy, then 3x10 pullups"
Start time as Unix milliseconds.
1783100000000
Elapsed duration in seconds.
1680
Moving time in seconds, when distinct from elapsed.
null
Distance in meters.
5000
Elevation gain in meters.
null
Active energy in kilocalories.
320
Average heart rate in bpm.
148
Max heart rate in bpm.
null
Average power in watts.
null
Average cadence (spm or rpm as reported).
null
Strength breakdown; empty for pure cardio.
Show child attributes
Show child attributes
[
{
"exercise": "pullups",
"sets": 3,
"reps": 10,
"weightLb": null,
"durationS": null,
"distanceM": null,
"notes": null
}
]
Row creation time as Unix milliseconds.
1783100001000