Writes

Create, update and delete athletes, coach assignments, teams, exercises, load-velocity profiles and injury reports, and retry creates safely with an Idempotency-Key.

The API also accepts the writes an external system, such as an athlete management system, a roster or a testing tool, needs to keep Vitruve in sync. Programs, workouts, templates and measurements are created in the Vitruve apps.

What you can write

ResourceEndpointsReference
AthletesPOST /athletes, PATCH /athletes/{id}, DELETE /athletes/{id}Organization and people
CoachesPUT /coaches/{id}/athletes, PUT /coaches/{id}/athletes/{athleteId}, DELETE /coaches/{id}/athletes/{athleteId}, DELETE /coaches/{id}Organization and people
TeamsPOST /teams, PATCH /teams/{id}, PUT /teams/{id}/athletes/{athleteId}, DELETE /teams/{id}/athletes/{athleteId}, DELETE /teams/{id}Organization and people
ExercisesPOST /exercises, PATCH /exercises/{id}, DELETE /exercises/{id}Catalogs
Load-velocity profilesPOST /equations, PATCH /equations/{id}, DELETE /equations/{id}Measurements
Injury reportsPOST /injury-reports, PATCH /injury-reports/{id}, DELETE /injury-reports/{id}Measurements

Rules

  • Only your organization's own API key writes. It writes on behalf of the organization, with the permissions its plan gives the owner.
  • The plan decides. Without an active subscription every write answers 403, and so does an action the plan doesn't include or whose limit is reached, such as the plan's athlete limit.
  • Send JSON with Content-Type: application/json. Unknown body fields answer 400, like unknown query parameters.
  • PATCH changes only the fields you send. Send null to clear an optional field. An empty body answers 400.
  • Ids in a body are checked. An athlete, coach, team, exercise, metric or muscle that isn't in your organization or in Vitruve's catalogs answers 400, and message names the field. An id in the path that isn't in your organization answers 404.
  • Dates in a body are a bare date (YYYY-MM-DD, read as midnight UTC) or an ISO 8601 datetime with Z or an offset, such as 2026-10-27T09:00:00+02:00. Anything else, a number, true or a loose string, answers 400.
  • Links (youtubeUrl, googleUrl) must be http or https.
  • Writes answer 200 with the created, updated or deleted resource, in the shape of the matching GET /…/{id}.
  • Deletes are permanent, as in the app, except exercises, which are archived so past workouts keep showing them.
  • Bodies are limited to 1 MB. A larger one answers 413.
  • Writes count toward the same rate limit as reads.

Some records can't be changed through the API and answer 403: the profile of an athlete who claimed their own Vitruve account (they manage it themselves), an exercise of Vitruve's library, and the organization's owner, who can't be removed.

Retry creates with an Idempotency-Key

Every POST that creates something accepts an Idempotency-Key header: 1 to 255 visible ASCII characters you choose per operation, such as a UUID or your own record's id. Send it to retry a create safely after a timeout or a dropped connection.

POST /v1/athletes
Idempotency-Key: roster-sync-2026-10-13-athlete-8812
CaseAnswer
First request with the keyRuns as usual. A 2xx answer is kept for an hour.
Same key, same request (method, path and body)The kept answer again, with an Idempotent-Replayed: true header. Nothing is created twice.
Same key, same request, when the first answer was over 32 KB409 This request already completed; fetch the resource instead. Nothing is created twice.
Same key while the first request is still running409. Retry after a moment.
Same key, a different request422.
The first request failed (any non-2xx answer)The key is released: a retry runs again.

Keys belong to the API key that sent them and expire an hour after the first request completes: retry within it. Without the header, a retried POST creates again. PATCH, PUT and DELETE don't take the header: repeating them leaves the same result.

  • Pick the key before the first attempt and store it with your own record, so every retry sends the same one. A new key per attempt creates again.
  • Resend the same bytes. The body is compared as sent: serialize it once and reuse the string, rather than rebuilding it with keys in another order.
  • A 503 runs nothing. If the key can't be recorded, the create answers 503 Idempotency store unavailable without running. Retry with backoff.
# Pick the key once per operation and reuse it on every retry.
curl --retry 5 --retry-all-errors -X POST "https://api-next.vitruve.fit/v1/athletes" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: roster-sync-athlete-8812" \
  -d '{ "name": "Lucas", "surname": "Moreau" }'

Errors on writes

StatusWhen
400A body field is missing, unknown or invalid, or an id in it isn't in your organization.
403No active subscription; the plan doesn't include the action or its limit is reached; a record the API can't change.
404The resource isn't in your organization. Injury reports and load-velocity profiles of an athlete who left it answer 404 too.
409The email belongs to another user, a record the write refers to was changed or removed meanwhile, or the first request with the same Idempotency-Key is still running or answered too large to keep.
413The body is larger than 1 MB.
422The Idempotency-Key was already used for a different request.

Every status, with its messages, is in Error codes.

On this page