Back to docs

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. 1. Go to Settings.
  2. 2. Open Agent access.
  3. 3. Select the scopes the agent needs.
  4. 4. Store the token in the agent's secret store.

Available scopes

Scopes are independent. Give OpenClaw only the actions it should perform.

read:todayread:weekread:contextwrite:wodwrite:checkinwrite:completionwrite:availabilitywrite:sorenesswrite:plannerwrite:profilewrite:racessync:refresh

Endpoints

GET/api/agent/todayread:today

Today workout and check-in status.

GET/api/agent/weekread:week

Current or requested training week.

GET/api/agent/contextread:context

Training context, upcoming plans, and deterministic target warnings.

POST/api/agent/wodwrite:wod

Save a confirmed supplementary-session description.

POST/api/agent/checkinwrite:checkin

Submit confirmed athlete-local daily readiness.

POST/api/agent/completionwrite:completion

Log confirmed workout completion status, RPE, and notes.

POST/api/agent/availabilitywrite:availability

Add confirmed blackout constraints up to 365 days.

POST/api/agent/sorenesswrite:soreness

Log confirmed soreness after a user check.

POST/api/agent/planner/movewrite:planner

Move a confirmed workout slot without recalibration.

PATCH/api/agent/profilewrite:profile

Update bounded profile fields and recalculate threshold-dependent TSS.

POST/api/agent/raceswrite:races

Create a confirmed race entry.

POST/api/agent/syncsync:refresh

Refresh Strava, Oura, and WHOOP 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 supplementary session before saving it.",
  "requiresConfirmation": true,
  "details": {
    "date": "2026-04-27",
    "confidence": 0.72,
    "uncertainItems": ["Workout date was not visible"]
  }
}
POST/api/agent/checkin

The route saves the confirmed athlete-local readiness first, then synchronously generates a workout when requested. A successful response reportsworkoutGeneration: generatedor skipped; it never returns an in-progress state.

If readiness saves but generation fails, the response is non-2xx withok: false,checkinSaved: true, and workoutGeneration: failed. Treat the readiness write as complete and use the generation error to decide whether a retry is appropriate.

GET/api/agent/today

Returns 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
  }
}
GET/api/agent/context

Returns recent fitness, workouts, activities, readiness, soreness, upcoming races, deterministic training-target warnings, 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. A zero-hour discipline target remains authoritative, but the response flags it when an A triathlon enters the 55-day race-proximity window. Coaching continuity is based on the latest swim, bike, or run; supplementary training still contributes load and soreness but cannot hide an endurance gap.

POST/api/agent/wod

This compatibility endpoint accepts descriptions of strength, conditioning, mobility/recovery, and other booked classes; it does not assume the session is CrossFit. The route and scope retain their legacy names so existing clients keep working. OpenClaw 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 supplementary-session 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.

POST/api/agent/planner/move

Moves a confirmed planner slot. The agent can move the whole day, the primary triathlon session, the secondary triathlon session, or the supplementary slot. Recalibration is not run automatically; moved sessions keep their existing content until the athlete explicitly recalibrates. Completed or synced planner days cannot be moved or overwritten.

{
  "fromDate": "2026-04-28",
  "toDate": "2026-04-30",
  "slot": "tri",
  "userConfirmed": true
}

Response fields

date

Athlete-local date used for lookup.

readiness.submitted

Whether a check-in row exists for the date.

readiness.overallFatigue

Athlete-entered fatigue score, or null if not submitted.

workout.exists

Whether a generated workout exists for the date.

workout.triSession

Primary endurance session, including title, content, duration, intensity, and TSS.

workout.secondTriSession

Optional second endurance session.

workout.wod

Optional supplementary-session content stored in the legacy WOD field.

workout.completionStatus

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

429 Generation or refresh rate limit reached; inspect whether a preceding write succeeded.

409 Confirmation required, date conflict, planner collision, or a confirmed resource changed before its write completed.

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, WHOOP, billing, or service-role credentials.
  • - Store tokens as secrets in the external agent, not in prompts or source control.