client.events manages the athlete's calendar: creating, updating and deleting planned workouts, notes, races and other calendar entries, individually or across a bulk date range. It also applies training plans to the calendar and downloads planned workouts as files.
| Method | What it does |
|---|---|
| EventService.listEvents | List events for a date range |
| EventService.getEvent | Get a specific event by ID |
| EventService.createEvent | Create a new event (workout, note, race, etc.) |
| EventService.createEventsBulk | Create multiple events at once |
| EventService.updateEvent | Update an existing event |
| EventService.updateEventsRange | Update events in a date range (move, shift, etc.) |
| EventService.deleteEvent | Delete an event |
| EventService.deleteEventsBulk | Delete events by ID list |
| EventService.deleteEventsRange | Delete events in a date range by category |
| EventService.markEventAsDone | Mark an event as done |
| EventService.applyPlan | Apply a training plan to the calendar |
| EventService.duplicateEvents | Duplicate events |
| EventService.downloadWorkout | Download a planned workout from the calendar in zwo, mrc, erg or fit format |
| EventService.listEventTags | Every tag that has been applied to events on the athlete's calendar |
| EventService.listFitnessModelEvents | Events that influence the fitness (CTL/ATL) calculation, in ascending date order |
| EventService.downloadWorkoutsZip | Calendar workouts in a date range as a zip of files in the requested format |
List planned workouts and races in the next 7 days, then list every tag that has been applied to a calendar event.
const client = new IntervalsClient({ apiKey });
const localDate = (d: Date) => `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;
const now = new Date(); // one timestamp for both bounds
const weekAhead = new Date(now);
weekAhead.setDate(weekAhead.getDate() + 6);
const oldest = localDate(now);
const newest = localDate(weekAhead);
const events = await client.events.listEvents({ oldest, newest, category: ['WORKOUT', 'RACE'] });
if (events.length === 0) {
console.log('No workouts or races in the next 7 days');
} else {
for (const event of events) {
console.log(`${event.start_date_local ?? ''} ${event.category ?? ''} ${event.name ?? ''}`);
}
}
const tags = await client.events.listEventTags();
console.log(tags.length > 0 ? `Event tags: ${tags.join(', ')}` : 'No event tags');
Create a NOTE event for today, update its description, then delete it.
CI never runs this example; running it by hand changes the authenticated account. If the create call itself fails after the server committed it, nothing is cleaned up: search the account for Created by the SDK example (or Uploaded by the SDK example) and delete it by hand.
// maxRetries: 0 — a create that committed before a 5xx would be repeated by a retry, and only the last response's id would be cleaned up.
const client = new IntervalsClient({ apiKey, maxRetries: 0 });
// The API keys calendar events by the athlete's local date, not the machine's. A machine
// in a different time zone (or a CI runner on UTC) would otherwise create the event on the
// wrong day, so the date is formatted in the athlete's zone (see
// examples/_shared/local-date.ts).
const me = await client.athletes.getAthlete();
const today = localDateIn(new Date(), me.timezone);
// A unique marker in the name: if the create fails after the server committed it, this
// is what to search the account for.
const marker = `Created by the SDK example ${Date.now()}`;
const event = await client.events.createEvent({
category: 'NOTE',
start_date_local: today,
name: marker,
});
if (typeof event.id !== 'number') {
console.error('The API did not return an id for the created event; it may need deleting by hand.');
process.exit(1);
}
const eventId = event.id;
console.log(`Created event id=${eventId}`);
try {
const updated = await client.events.updateEvent(eventId, { description: 'Edited by the SDK example' });
console.log(`Updated description: ${updated.description}`);
} finally {
await client.events.deleteEvent(eventId);
console.log(`Deleted event ${eventId}`);
}
List the events that influence the fitness (CTL/ATL) calculation, then download the next 30 days of planned workouts as a zip of files.
const client = new IntervalsClient({ apiKey });
const events = await client.events.listFitnessModelEvents();
console.log(`${events.length} fitness-model event(s)`);
for (const event of events.slice(0, 3)) {
console.log(`${event.start_date_local ?? ''} ${event.name ?? ''}`);
}
const localDate = (d: Date) => `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;
const now = new Date(); // one timestamp for both bounds
const monthAhead = new Date(now);
monthAhead.setDate(monthAhead.getDate() + 29);
const oldest = localDate(now);
const newest = localDate(monthAhead);
const upcomingWorkouts = await client.events.listEvents({ oldest, newest, category: ['WORKOUT'] });
if (upcomingWorkouts.length === 0) {
console.log('No workout events to zip up in the next 30 days');
} else {
const zip = await client.events.downloadWorkoutsZip({ ext: '.zwo', oldest, newest });
console.log(`Workouts zip: ${zip.length} bytes`);
}
ext query value has no leading dot (the API expects zwo, mrc, erg or fit), unlike the {ext} path suffix on EventService.downloadWorkout. See API behaviour.