API Reference
Public endpoints, request shape, response structure, and live schema access for Power Oracle.
Live Endpoints
- Live API base: https://api.workcapacity.io
- API catalog: https://api.workcapacity.io/.well-known/api-catalog
- OpenAPI schema: https://api.workcapacity.io/openapi.json
- Payment requirements: https://api.workcapacity.io/v1/payment-requirements
Agents and software systems should use the OpenAPI schema directly.
Browsable Swagger and ReDoc interfaces are local-development tooling and are not part of the deployed public contract.
The current public contract is versioned under /v1. External users should integrate against the /v1/* routes.
Current Public Endpoints
GET /v1/healthGET /v1/movementsGET /v1/payment-requirementsGET /v1/athletes/{athlete_uuid}/workoutsGET /v1/athletes/{athlete_uuid}/workouts/{workout_id}GET /v1/athletes/{athlete_uuid}/curvePOST /v1/compute-powerPOST /v1/workouts/\{workout_id\}/revisionsPOST /v1/workouts/\{workout_id\}/void
GET /v1/health
Use this as a liveness check.
Example:
curl https://api.workcapacity.io/v1/health
GET /v1/movements
This endpoint returns the public movement registry that clients should consult before building compute requests.
Use it to discover:
- supported movement names
- required inputs
- alternative input rules
- supported overrides
- public modeling assumptions
Why it matters:
- It tells you which movements the system supports right now.
- It tells you which inputs are required versus optional.
- It helps an agent refuse to guess when a movement or required input is missing.
- It keeps clients aligned with the current movement library instead of hard-coding stale assumptions.
Sample Response Fragment
{
"air_squat": {
"name": "air_squat",
"required_inputs": [],
"supported_overrides": ["height_coefficient"]
},
"thruster": {
"name": "thruster",
"required_inputs": [
{
"name": "external_load",
"required": true,
"allowed_units": ["kg", "lb"]
}
],
"supported_overrides": ["height_coefficient", "load_height_coefficient"]
},
"row": {
"name": "row",
"required_inputs": [],
"alternative_inputs": [
{
"description": "Provide either rowing distance or displayed calories.",
"options": [
{ "name": "travel_distance", "allowed_units": ["m", "ft"] },
{ "name": "calories", "allowed_units": ["kcal"] }
]
}
]
}
}
Required vs Optional Inputs
air_squatneeds no movement-specific input.thrusterrequiresinputs.external_load.rowrequires exactly one alternative input:travel_distanceorcalories.- Overrides are optional and should only be sent when the client explicitly intends to depart from the default assumption.
GET /v1/payment-requirements
This endpoint returns the current public payment metadata for paid routes.
Use it to discover:
- which routes are currently paid
- the public display price for each route
- the currently resolved L402 sat amount when a BTC/USD quote is available
- the route policy identifier
- any public Bazaar metadata that describes the paid route
Sample Response Fragment
{
"routes": [
{
"method": "POST",
"path": "/v1/compute-power",
"policy_id": "compute-power-v2",
"protocol": "l402",
"payment_mode": "single_use",
"price": "$0.10",
"price_sats": 100
},
{
"method": "POST",
"path": "/v1/compute-power",
"policy_id": "compute-power-v2",
"protocol": "x402",
"payment_mode": "single_use",
"price": "$0.10"
},
{
"method": "POST",
"path": "/v1/workouts/{workout_id}/revisions",
"policy_id": "workout-revision-v1",
"protocol": "l402",
"payment_mode": "single_use",
"price": "$0.04",
"price_sats": 40
},
{
"method": "POST",
"path": "/v1/workouts/{workout_id}/revisions",
"policy_id": "workout-revision-v1",
"protocol": "x402",
"payment_mode": "single_use",
"price": "$0.04"
}
]
}
GET /v1/athletes/{athlete_uuid}/workouts
Returns an athlete's active canonical completed workouts, newest first. The athlete_uuid is a capability token. No bearer token or payment challenge is required.
Query parameters:
limit: page size,1-100, default20cursor: opaque next-page cursor from the previous responsesince: only workouts on or after this performed dateuntil: only workouts on or before this performed datedomain:short,medium, orlongmovement: exact movement name fromGET /v1/movementsupdated_since: only workouts updated at or after this UTC timestamp
The response includes total, has_more, cursor, and items[]. total is the count of active canonical workouts matching the supplied filters.
Each item includes workout_id, revision_id, revision_number, performed_date, updated_at, duration, total work, active average power, split count, rest flag, movement names, and notes.
GET /v1/athletes/{athlete_uuid}/workouts/{workout_id}
Returns one active canonical workout detail record for the given athlete. This is the route agents should use before deciding whether a past workout needs a correction or void.
The response includes:
workoutmetadata with canonicalworkout_id,revision_id, andrevision_numberperformed_dateandupdated_atresults.sessionresults.splits[]results.summaryresults.movement_rollups[]notes
The route returns 404 when the athlete is unknown, the workout is unknown, the workout belongs to a different athlete, or the workout has been voided.
GET /v1/athletes/{athlete_uuid}/curve
Returns a bounded power-duration curve for active canonical completed workouts. This route is public and uuid-gated.
Query parameters:
since: only workouts on or after this performed dateuntil: only workouts on or before this performed datemax_points: maximum newest matching workout points to process and return, default500, maximum1000include_points:allorenvelopetrend_window_days: recent trend window, default14, maximum60trend_baseline_days: baseline window before the recent window, default90, range7-365
The response includes:
points: returned workout points sorted by duration ascendingenvelope_points: Pareto-optimal points sorted by duration ascendingwork_capacity_auc: area under the returned envelope in joules, using trapezoidal integration over linear durationdomain_slices: short, medium, and long bestsdomain_trends: recent-versus-baseline trends computed within each duration domain
work_capacity_auc is a Power Oracle work-capacity estimate over the selected response window. It is aligned with CrossFit's work-capacity curve framing, but it is only as complete as the athlete's logged coverage across time and modal domains.
POST /v1/compute-power
POST /v1/compute-power accepts a session-first, split-aware request body.
Required Top-Level Fields
athlete_uuidevaluation_contextduration_secondsusersplits
Context Rules
completedrequiresperformed_dateplannedrequiresplanned_for_dateplannedandhypotheticalmay includescenario_labelhypotheticalmust not includeperformed_dateorplanned_for_date
User Object
heightbody_mass- optional
age_years - optional
sex - optional
response_units
Each Split Includes
- optional
label - active
duration_seconds - optional
rest_seconds_after work.movements[]
Time Rules
- Top-level
duration_secondsis session elapsed time. - Split
duration_secondsis split active time. rest_seconds_afteris recovery after that split.- If session elapsed time exceeds accounted split time, the response returns
unattributed_duration_seconds.
Example Request
{
"athlete_uuid": "11111111-1111-1111-1111-111111111111",
"evaluation_context": "completed",
"performed_date": "2026-03-20",
"duration_seconds": 133,
"user": {
"height": { "value": 70, "unit": "in" },
"body_mass": { "value": 180, "unit": "lb" },
"age_years": 35,
"sex": "male"
},
"splits": [
{
"label": "21 thrusters",
"duration_seconds": 33,
"work": {
"movements": [
{
"movement": "thruster",
"reps": 21,
"inputs": {
"external_load": { "value": 95, "unit": "lb" }
},
"spec_overrides": {}
}
]
}
},
{
"label": "21 pull-ups",
"duration_seconds": 27,
"work": {
"movements": [
{
"movement": "pull_up",
"reps": 21,
"spec_overrides": {}
}
]
}
}
]
}
Response Shape
POST /v1/compute-power returns:
- optional
workout results.sessionresults.splits[]results.summaryresults.movement_rollups[]notes
Important semantics:
results.sessionreports session totals and elapsed/session-active powerresults.splits[]reports per-split work and active-time powerresults.summarycaptures peak, minimum, mean split power, dropoff, and consistencyresults.movement_rollups[]preserves movement totals without inventing per-movement timing inside grouped splitsnotesexplains denominator and modeling assumptions
Stored Completed Workout Metadata
When evaluation_context=completed, successful POST /v1/compute-power responses now include:
workout.workout_idworkout.revision_idworkout.revision_numberworkout.revision_statusworkout.supersedes_revision_id
Additional Details
X-PowerOracle-Agentis an optional request header and is stored with successful compute events.api_versionandcompute_model_versionare server-managed and should not be sent by clients.- In MVP, only successful
completedrequests create stored workout history.plannedandhypotheticalrequests compute results but do not create stored workouts.
POST /v1/workouts/{workout_id}/revisions
Use this route to correct a stored completed workout.
Required fields:
supersedes_revision_idcompute_request
Optional fields:
correction_reason
Rules:
compute_request.evaluation_contextmust becompletedcompute_request.athlete_uuidmust match the athlete for the target workoutsupersedes_revision_idmust be the current canonical revision for that workout
If the supplied revision is stale, the route returns 409 Conflict.
Successful responses use the same workout + results + notes shape as POST /v1/compute-power.
POST /v1/workouts/{workout_id}/void
Use this route when the current canonical workout should stop counting and there is not yet a corrected replacement.
Required fields:
supersedes_revision_idvoid_reason
Behavior:
- the current canonical revision becomes
voided - the workout is removed from default history and analytics
- no new compute result is created
This route returns workout state only, not compute results.
Example response:
{
"workout": {
"workout_id": "11111111-1111-1111-1111-111111111111",
"workout_status": "voided",
"voided_revision_id": "22222222-2222-2222-2222-222222222222"
}
}
Paid Route Behavior
In production, compute-bearing routes are paid:
POST /v1/compute-powerPOST /v1/workouts/\{workout_id\}/revisions
Operational rules:
- first call may return
402 Payment Required - the x402 challenge arrives in
PAYMENT-REQUIRED - the L402 challenge arrives in
WWW-Authenticate: L402 ... - retry with
PAYMENT-SIGNATURE: <x402 proof>orAuthorization: L402 <token>:<preimage> - successful x402 responses include
PAYMENT-RESPONSE - route price is
$0.10forPOST /v1/compute-powerand$0.04forPOST /v1/workouts/\{workout_id\}/revisions - x402 uses the USD-denominated exact price directly
- L402 uses sats derived from the same USD route price with a cached live BTC/USD quote
- authorized
2xxand4xxresponses settle or consume the payment, while5xxresponses do not POST /v1/workouts/\{workout_id\}/voidis free- If the live challenge or server behavior differs, treat the live server behavior as authoritative.
See Workout History And Corrections for the rationale and workflow, Calling Power Oracle from a Client for the operational flow, and Payments for the payment contract.
