{% extends "newbase.html" %} {% block title %}Rowsandall Developers Info{% endblock title %} {% block main %}
On this page, I will collect useful information for developers of rowing data apps and hardware.
I presume you have an app (smartphone app, dedicated hardware, web site) where your users (customers) generate, collect or store their rowing related workout data. You can now offer your users easy ways to get their data on this site.
There are three ways to allow your users to get data to Rowsandall.com.
Enable export of TCX, FIT or CSV formatted files from your app. The users upload the file to Rowsandall.com.
Similar as above, generate TCX, FIT or CSV formatted files and email them to workouts@rowsandall.com directly from your app. The From: field should be the email address of the registered user.
We have a REST API which will allow you to post and receive stroke data from the site directly.
We are open to improvement suggestions (provided they don't break existing apps). Please send email to info@rowsandall.com with questions and/or suggestions. We will get back to you as soon as possible.
All files adhering to the standards TCX and FIT formats will be parsed.
However, some rowing related parameters are not supported by TCX and FIT. Therefore, we are supporting the CSV format that is documented in the following link.
Using this standard will guarantee that your user's data are accepted without complaints.
We have disabled the self service app link for security reasons. If you need to register an app, please send email to info@rowsandall.com
Standard Oauth2 authentication. Get authorization code by pointing your user to the authorization URL. Exchange authorization code for an access token. When access token expires, use the refresh token to refresh it.
The redirect URI for user authentication has to be https. Developers of iOS or Android apps should contact me directly if this doesn't work for them. I can add exceptions.
Once you have a registered app, you have gone through the authorization and have successfully obtained an access token, you can use it to place POST, GET and PUT requests.
The workout summary data and the stroke data are obtained and sent separately.
In the table below {id} indicates the ID of the item.
| Item | Access Points | Permissions | Example |
|---|---|---|---|
| Workout | /rowers/api/workouts/
/rowers/api/workouts/{id} |
GET, POST, DELETE |
{
"id": 18,
"name": "2x",
"date": "2020-10-15",
"workouttype": "water",
"starttime": "08:40:50",
"distance": 9751,
"duration": "00:52:40",
"averagehr": 104,
"maxhr": 122,
"notes": "",
"summary": "Workout Summary - media/97ec07a851-20201015-192157-95763ffd-fd4e-4b5f-bf5f-8a8495c16639.csv\n--|Total|-Total----|--Avg--|-Avg-|Avg-|-Avg-|-Max-|-Avg\n--|Dist-|-Time-----|-Pace--|-Pwr-|SPM-|-HR--|-HR--|-DPS\n--|09751|00:52:40.0|02:42.0|173.0|18.0|104.4|122.0|10.3\nW-|09751|00:52:40.0|02:42.0|173.0|18.0|104.4|122.0|10.3\nR-|00000|00:00:00.0|00:00.0|000.0|00.0|000.0|122.0|00.0\nWorkout Details\n#-|SDist|-Split-|-SPace-|-Pwr-|SPM-|AvgHR|MaxHR|DPS-\n00|09751|52:40.0|02:42.0|173.0|18.0|104.4|122.0|10.3\n",
"boattype": "2x",
"timezone": "Europe/Amsterdam",
"forceunit": "N",
"inboard": 0.88,
"oarlength": 2.89,
"privacy": "visible",
"rankingpiece": false
}
|
| Favorite Chart | /rowers/api/charts/
/rowers/api/charts/{id} |
GET, DELETE |
{
"id": 7,
"xparam": "time",
"yparam1": "hr",
"yparam2": "power",
"plottype": "line",
"workouttype": "otw",
"reststrokes": true,
"user": 1
}
|
| Planned Session | /rowers/api/plannedsessions/
/rowers/api/plannedsessions/{id} |
GET, POST, DELETE |
{
"id": 103,
"name": "ExampleSession",
"comment": "",
"startdate": "2021-03-01",
"enddate": "2021-03-06",
"preferreddate": "2021-03-01",
"sessiontype": "session",
"sessionvalue": 74,
"sessionunit": "min",
"sessionmode": "time",
"course": null,
"approximate_distance": 16440,
"approximate_duration": 74,
"fitfile": "https://rowsandall.com/media/a7a7a621ad-20210302-074033-tmp2zcd7i_0.fit"
}
|
| Challenge | /rowers/api/challenges/
/rowers/api/challenges/{id} |
GET |
{
"id": 1,
"name": "Alphen",
"course": 1,
"registration_closure": "2020-10-18T08:45:00+02:00",
"evaluation_closure": "2020-10-18T12:16:00+02:00",
"start_time": "08:45:00",
"end_time": "12:00:00",
"country": "Nederland",
"timezone": "Europe/Amsterdam",
"contact_phone": "",
"contact_email": "",
"entries": [
{
"id": 1,
"username": "Sander Roosendaal",
"teamname": null,
"boattype": "1x",
"sex": "male",
"age": 48,
"adaptiveclass": "None",
"skillclass": "Open",
"coursecompleted": false,
"distance": 0,
"duration": "00:00:00",
"points": 0.0,
"entrycategory": null
}
],
"coursestandards": null
}
|
| Challenge Entry | /rowers/api/entries/
/rowers/api/entries/{id} |
GET, POST |
{
"id": 1,
"teamname": null,
"adaptiveclass": "None",
"skillclass": "Open",
"race": {
"id": 1,
"name": "Alphen",
"course": 1,
"registration_closure": "2020-10-18T08:45:00+02:00",
"evaluation_closure": "2020-10-18T12:16:00+02:00",
"start_time": "08:45:00",
"end_time": "12:00:00",
"country": "Nederland",
"timezone": "Europe/Amsterdam",
"contact_phone": "",
"contact_email": "",
"entries": [
{
"id": 1,
"username": "Sander Roosendaal",
"teamname": null,
"boattype": "1x",
"sex": "male",
"age": 48,
"adaptiveclass": "None",
"skillclass": "Open",
"coursecompleted": false,
"distance": 0,
"duration": "00:00:00",
"points": 0.0,
"entrycategory": null
}
],
"coursestandards": null
},
"distance": 0,
"duration": "00:00:00",
"points": 0.0,
"boattype": "1x",
"sex": "male",
"age": 48,
"entrycategory": null
}
|
| Course | /rowers/api/geocourses/
/rowers/api/geocourses/{id} |
GET |
Example shortened for brevity (omitted gates 2-14):
{
"id": 1,
"name": "Alphen - Alphen aan den Rijn",
"distance": 14847,
"country": "Nederland",
"notes": "\n\n",
"polygons": [
{
"id": 1,
"name": "Start",
"order_in_course": 0,
"points": [
{
"id": 1,
"latitude": 52.14611068342334,
"longitude": 4.704149601313898,
"order_in_poly": 0
},
{
"id": 2,
"latitude": 52.14606840788696,
"longitude": 4.704648516706039,
"order_in_poly": 1
},
{
"id": 3,
"latitude": 52.14626893773362,
"longitude": 4.704642182077736,
"order_in_poly": 2
},
{
"id": 4,
"latitude": 52.14628828501986,
"longitude": 4.704151599747837,
"order_in_poly": 3
},
{
"id": 5,
"latitude": 52.14611068342334,
"longitude": 4.704149601313898,
"order_in_poly": 4
}
]
},
{
"id": 15,
"name": "Finish",
"order_in_course": 14,
"points": [
{
"id": 71,
"latitude": 52.1461319705247,
"longitude": 4.70414987291546,
"order_in_poly": 0
},
{
"id": 72,
"latitude": 52.14607111930849,
"longitude": 4.704561170436561,
"order_in_poly": 1
},
{
"id": 73,
"latitude": 52.14626893773362,
"longitude": 4.704642182077736,
"order_in_poly": 2
},
{
"id": 74,
"latitude": 52.14628831020436,
"longitude": 4.70415735390207,
"order_in_poly": 3
},
{
"id": 75,
"latitude": 52.1461319705247,
"longitude": 4.70414987291546,
"order_in_poly": 4
}
]
}
]
}
|
| Course Time Standard Collection | /rowers/api/standardcollections/
/rowers/api/standarcollections/{id} |
GET |
Example shortened for brevity (showing only first three):
{
"id": 1,
"name": "Charles River Times Standards",
"notes": "",
"active": true,
"standards": [
{
"id": 1,
"name": "M1x",
"coursedistance": 4700,
"coursetime": "17:15.0",
"agemin": 0,
"agemax": 120,
"boatclass": "water",
"boattype": "1x",
"sex": "male",
"weightclass": "hwt",
"adaptiveclass": "None",
"skillclass": "Open",
"standardcollection": 1
},
{
"id": 2,
"name": "MLW1x",
"coursedistance": 4700,
"coursetime": "17:15.0",
"agemin": 0,
"agemax": 120,
"boatclass": "water",
"boattype": "1x",
"sex": "male",
"weightclass": "lwt",
"adaptiveclass": "None",
"skillclass": "Open",
"standardcollection": 1
},
{
"id": 3,
"name": "MYouth1x",
"coursedistance": 4700,
"coursetime": "18:30.0",
"agemin": 15,
"agemax": 18,
"boatclass": "water",
"boattype": "1x",
"sex": "male",
"weightclass": "hwt",
"adaptiveclass": "None",
"skillclass": "Open",
"standardcollection": 1
},
]
}
|
| Individual Course Time Standard | /rowers/api/standards/
/rowers/api/standards/{id} |
GET, POST |
{
"id": 1,
"name": "M1x",
"coursedistance": 4700,
"coursetime": "17:15.0",
"agemin": 0,
"agemax": 120,
"boatclass": "water",
"boattype": "1x",
"sex": "male",
"weightclass": "hwt",
"adaptiveclass": "None",
"skillclass": "Open",
"standardcollection": 1
}
|
| Stroke Data (v1) | /rowers/api/workouts/{id}/strokedata | GET, POST |
{
"distance": [23, 46, 48],
"time": [3200, 6700, 10099],
"spm": [16.4, 21.2, 19.8],
"pace": [155068, 144402, 138830],
"power": [84.6, 117.2, 141.3],
"hr": [85, 91, 95]
}
You can only post stroke data to an existing workout with
workout number {id}. If the workout already has stroke data, you
will get a duplication error. This functionality will be expanded in the
future to enable updating stroke data.
For both v1 and v2, mandatory data fields are:
Optional data fiels are:
For both API versions, consistency checks will be done and the stroke data will be refused if the mandatory data fields don't pass the checks. All mandatory data fields must have the same number of records. If an optional data field fails a test, its values are silently replaced by zeros. |
| Stroke Data (v2) | /rowers/api/v2/workouts/{id}/strokedata | GET, POST |
{
"data": [
{
"time": 3200.0000476837,
"pace": 155068.4885951763,
"hr": 85.7857142857,
"power": 84.6531131591,
"distance": 23,
"spm": 16.380952381
},
{
"time": 6700.0000476837,
"pace" : 144402.6407586741,
"hr": 91.2142857143,
"power": 117.458827834,
"distance": 36,
"spm": 21.1666666667
},
{
"time": 10099.9999046326,
"pace": 138830.8712654931,
"hr": 95.7142857143,
"power": 141.31057207,
"distance": 48,
"spm": 19.8095238095
}
]
}
You can only post stroke data to an existing workout with
workout number {id}. If the workout already has stroke data, you
will get a duplication error. This functionality will be expanded in the
future to enable updating stroke data.
For both v1 and v2, mandatory data fields are:
Optional data fiels are:
For both API versions, consistency checks will be done and the stroke data will be refused if the mandatory data fields don't pass the checks. All mandatory data fields must have the same number of records. If an optional data field fails a test, its values are silently replaced by zeros. |
| Stroke Data (v3) | /rowers/api/v3/workouts/ | POST |
{
"distance": 100,
"elapsedTime": 29000,
"duration": "0:00:29.0",
"name": "Test Workout (GO)",
"startdatetime": "2023-01-16 17:54:35.588838+00:00",
"workouttype": "water",
"boattype": "1x",
"notes": "some\nnotes",
"strokes": {
"data": [
{
"time": 3200.0000476837,
"pace": 155068.4885951763,
"hr": 85.7857142857,
"power": 84.6531131591,
"distance": 23,
"spm": 16.380952381
},
{
"time": 6700.0000476837,
"pace" : 144402.6407586741,
"hr": 91.2142857143,
"power": 117.458827834,
"distance": 36,
"spm": 21.1666666667
},
{
"time": 10099.9999046326,
"pace": 138830.8712654931,
"hr": 95.7142857143,
"power": 141.31057207,
"distance": 48,
"spm": 19.8095238095
}
]
}
}
With v3, you can post stroke data and workout metadata in one JSON object.
For v3, mandatory data fields are:
Optional data fiels are:
Also for this API version, consistency checks will be done and the stroke data will be refused if the mandatory data fields don't pass the checks. All mandatory data fields must have the same number of records. If an optional data field fails a test, its values are silently replaced by zeros. |