Developer docs
Agent API
Give an external AI agent narrow access to training context and confirmed actions. The app stays the source of truth; the agent supplies structured requests and the API validates them.
Authentication
Requests use a bearer token created from Settings. Tokens start withtra_live_and are stored in the database as hashes. The full token is only shown once.
curl https://training.cjarhodes.com/api/agent/today \
-H "Authorization: Bearer tra_live_..." \
-H "Accept: application/json"Create a token
In the app
- 1. Go to Settings.
- 2. Open Agent access.
- 3. Select the scopes the agent needs.
- 4. Store the token in the agent's secret store.
Available scopes
Scopes are independent. Give OpenClaw only the actions it should perform.
Endpoints
GET/api/agent/todayread:todayToday workout and check-in status.
GET/api/agent/weekread:weekCurrent or requested training week.
GET/api/agent/contextread:contextTraining context for a recent range and upcoming plan.
POST/api/agent/wodwrite:wodSave confirmed imported WOD text.
POST/api/agent/checkinwrite:checkinSubmit athlete-local daily readiness.
POST/api/agent/completionwrite:completionLog workout completion status, RPE, and notes.
POST/api/agent/availabilitywrite:availabilityAdd confirmed blackout/availability constraints.
POST/api/agent/sorenesswrite:sorenessLog confirmed soreness after a user check.
POST/api/agent/planner/movewrite:plannerMove a confirmed workout slot without recalibration.
PATCH/api/agent/profilewrite:profileUpdate confirmed profile fields.
POST/api/agent/raceswrite:racesCreate a confirmed race entry.
POST/api/agent/syncsync:refreshRefresh Strava and Oura data with rate limiting.
Confirmation model
The API does not create a normal pending-review queue. When a write is ambiguous or not explicitly confirmed, the route returns 409 confirmation_required. OpenClaw should ask the athlete in chat, then retry the same request withuserConfirmed: true.
{
"ok": false,
"error": "confirmation_required",
"message": "Confirm the extracted WOD before saving it.",
"requiresConfirmation": true,
"details": {
"date": "2026-04-27",
"confidence": 0.72,
"uncertainItems": ["Workout date was not visible"]
}
}/api/agent/todayReturns the athlete-local date, readiness/check-in status, and the workout generated for that date. Responses are marked Cache-Control: no-store.
{
"ok": true,
"date": "2026-04-26",
"timezone": "America/Chicago",
"user": {
"id": "f3b8...",
"email": "alex@example.com"
},
"readiness": {
"submitted": true,
"submittedAt": "2026-04-26T12:03:11.000Z",
"trainedAlready": false,
"overallFatigue": 3,
"motivation": 4,
"currentlyIll": false,
"illnessDays": null,
"returningFromIllness": false,
"notes": "Slept well. Legs a little heavy."
},
"workout": {
"exists": true,
"restDay": false,
"completionStatus": null,
"triSession": {
"type": "run",
"title": "Easy aerobic run",
"durationMinutes": 70,
"tss": 65,
"intensity": "Zone 1-2"
},
"secondTriSession": null,
"wod": null
}
}/api/agent/contextReturns recent fitness, workouts, activities, readiness, soreness, upcoming races, and open attention items. Use rangeDaysto request 7 to 90 days of recent history. Upcoming workouts and races are included so the agent can reason about schedule changes before asking for confirmation.
/api/agent/wodOpenClaw should perform OCR/vision itself, ask for confirmation when details are unclear, then submit text only. The app does not accept screenshots on this endpoint.
{
"date": "2026-04-27",
"source": "openclaw",
"rawText": "exact OCR text from the screenshot",
"cleanText": "cleaned WOD text, preserving original structure",
"confidence": 0.93,
"userConfirmed": true,
"uncertainItems": []
}If confidence is below 0.85 or uncertainItems is not empty, the request returns confirmation_requiredunless userConfirmed is true.
/api/agent/planner/moveMoves a confirmed planner slot. The agent can move the whole day, the primary triathlon session, the secondary triathlon session, or the WOD. Recalibration is not run automatically; moved sessions keep their existing content until the athlete explicitly recalibrates.
{
"fromDate": "2026-04-28",
"toDate": "2026-04-30",
"slot": "tri",
"userConfirmed": true
}Response fields
dateAthlete-local date used for lookup.
readiness.submittedWhether a check-in row exists for the date.
readiness.overallFatigueAthlete-entered fatigue score, or null if not submitted.
workout.existsWhether a generated workout exists for the date.
workout.triSessionPrimary endurance session, including title, content, duration, intensity, and TSS.
workout.secondTriSessionOptional second endurance session.
workout.wodOptional CrossFit content if there is a prescribed or group WOD.
workout.completionStatusCurrent workout completion state if logged in the app.
How the right user is selected
The agent does not send an email address, user id, or search query. The bearer token is hashed, matched against the agent_tokenstable, and the matched row supplies the user id. That user id is then used for reads and writes.
Errors
Error responses use the same shape across the endpoint.
{
"ok": false,
"error": "Invalid bearer token"
}401 Missing, invalid, revoked, or expired token.
402 Subscription required.
403 Token does not have the required scope.
409 Confirmation required, date conflict, or planner collision.
500 Lookup failed.
Security model
- - Tokens are hashed before storage.
- - Tokens are scoped by capability.
- - Tokens are revocable from Settings.
- - Agent writes are logged to an audit table.
- - Low-confidence or unconfirmed writes return a confirmation-required response.
- - The endpoint does not expose Strava, Oura, billing, or service-role credentials.
- - Store tokens as secrets in the external agent, not in prompts or source control.