The Aidoo API provides a growing set of highly flexible features for data exchange:
This document serves both as the documentation for the released features as well as a documentation for features that are currently in development. Features are thefore marked with (Released), (Beta) and (Development).
If you encounter problems check the section for common problems!
The Aidoo API is a REST-ful JSON-based API that strives to expose as many Aidoo-features as possible to our partners in a well documented form.
The vision of the API project is to give partners an intuitive and easy to use access to the Aidoo Ecosystem, but in a way that the flexibility, nuances and specialized features of the Aidoo System are preserved. To make this possible, the complex multi-table datastructures of the Aidoo Databases are abstracted into broader, more intuitive entities: Members, Contracts, Gyms, Classes, Bookings and more. These entities live in a graph of foreign-key relationships: Gyms can have multiple Members, Members have multiple contracts etc. Inspired by GraphQL, the API allows to query, insert, update and delete entities in a way, that queries and modifications can be extended to and filtered by properties of related entities along the graph. This is achieved without having the overhead of another complex language like GraphQL, but just by using intuitive parameters and paths in a pure REST-like approach.
Here some examples:
GET specific member
<baseurl>/v2/member/202200012
{
"success": true,
"members": [{... member data ...}]
}
POST new member with a new contract
<baseurl>/v2/members
{
"members": [{
"gym_id": ...,
"name": ...,
...
"contracts": [{
"contract_type_id": ...,
...
}]
}]
}
For now, <baseurl>
is https://api.aidoo-online.de:10015
.
The port 10015 is currently required for the api to work, we are working to free up the port 443 on the server soon to have a link without the port. (Development)
The API constrains access to features via api key scopes. We want to limit the security risks and therefore expose a minimal sufficient subset of the features to each partner. (Development)
The API uses scoped bearer token api keys to manage access to clients and features. The client is automatically identified by the api key and the features of the api are limited to the scope given to the api key. For security, we want to expose only the most powerful features for the actual usecases of a partner but not more. The key ist just a bearer token, so it can be used by adding a Header field Authorization: Bearer xxxx
to each request. In order to receive an API Key, contact our CEO Burkhard Westermann (westermann@aidoo.de) for an initial partnership meeting to negotiate the conditions for using the API.
<base_url>/v2/<entityname>s
Write the entity name in plural form unless you use an implicit filter.For GET and DELETE requests, each entity exposes a set of url parameters to add filters, include special data and extend to related entities.
For POST requests, request parameters are not required, as all information is contained in the json data. Replace <entityname>
with the entity you want to insert or update, e.g. "member" or "contract". You can batch insert/update resources by adding multiple entries to the array. Always wrap your entity in a root object like this:
{
"<entityname>s": [{...json model...}, {...}, ...]
}
Same as request structure but with a success flag and a message if an error occurs.
{
"<entityname>s": [{...}, {...}, ...],
"success": false,
"message": "Something specific went wrong"
}
The API supports filtering on properties along the entity graph, using url path and parameters. There are filters for equality, max, min, exclusive max, exclusive min and being in a list of elements. The type of the filter is identified by a prefix, the navigation through the graph is done by further prefixes to the fieldname using the traversed entity names.
Filter prefixes:
- <property>: equals
- min_<property>
- max_<property>
- emin_<property> - exclusive min
- emax_<property> - exclusive max
- in_<property> - followed by a csv list of ids/dates/values
Tipp: Writing the entity name in singular form in the path allows adding an implicit filter to the path on the id.
Examples:
<baseurl>/v2/members?id=202200012
is the same as
<baseurl>/v2/member/202200012
min/max filters:
Classes between 2024-02-12 (incl) and 2024-02-19 (excl)
<baseurl>/v2/classes?min_begin=2024-02-12&emax_begin=2024-02-19
filter along the graph:
Trainers whose classes member 202200022 attended
<baseurl>/v2/trainers?class_attendee_id=202200022
-> 'class' joins the classes of the trainer
-> 'attendee' joins the attendee members of the class
-> 'id' adds a filter on the id of the class attendee member
This only adds filters on the joined data, the result still only contains the trainer data. Add extensions to also include the class data in the results!
As mentioned before, entities can extend to related entities along the graph. The related entities are then joined in the result set as subobjects of the root entities. This allows high flexibility for querying data in an intuitive way.
<baseurl>/v2/member/202200022?with_class_trainers
{
"members": [{
... member 1 data ...
"classes": [{
... class 1 data ...
"trainer": {
... trainer data ...
}
},{
... class 2 data ...
"trainer": {
... trainer data ...
}
}]
}]
}
-> Singular form of endpoint expects id in the next path section
Example: /member/202202022 is the same as /members?id=202202022
Solution: Use plural form or set the id in the next path section
Always wrap you json data into the request structure:
{
"members": [
{...member json structure...}
]
}
In general use the entity name in plural form as
-> The api key is not configured to a backend
Solution:
(Released)
Supported methods:
Get all gyms:
GET https://api.aidoo-online.de:10015/v2/gyms
{
"success": true,
"gyms": [{...gym1 json...}, {...gym2 json...}, ...]
}
{
"id": 25,
"name": "Testgym",
"street": "Alte Poststraße",
"houseNumber": "116a",
"zip": "46514",
"city": "Schermbeck",
"phone": "123456789",
"email": "test@test.de"
}
Supported methods:
Get a member:
GET <base_url>/v2/member/202202022
or
GET <base_url>/v2/members?id=202202022
Result:
{
"member": [
{... member data ...}
]
}
Add a member:
POST <base_url>/v2/members
{
"members": [{
"address_city":"",
"address_country":"Germany",
"address_number":"",
"address_street":"",
"address_zip":"",
"birthday":"2022-03-21",
"email":"a.b@c.de",
"firstname":"Rudolph",
"gym_id":2,
"is_lead":false,
"mobile":"",
"name":"Plitt",
"phone":"",
"sepa_bankname":"",
"sepa_bic":"",
"sepa_iban":"",
"sex":"f"
}]
}
{
"address_city":"",
"address_country":"Germany",
"address_number":"",
"address_street":"",
"address_zip":"",
"birthday":"2022-03-21",
"email":"a.b@c.de",
"firstname":"Rudolph",
"gym_id":2,
"id":202200055,
"is_lead":false,
"mobile":"",
"name":"Plitt",
"phone":"",
"sepa_bankname":"",
"sepa_bic":"",
"sepa_iban":"",
"sex":"f"
}
Supported methods:
{
"bonus_credit":0,
"bonus_days":0,
"bonus_months":0,
"cancel_days":28,
"duration_days":182,
"extend_days":0,
"extend_months":3,
"extend_price":0,
"id":58,
"name":"YOU ABO 25",
"payment_days":7,
"payment_dynamic":
{
"duration_days":0,
"duration_months":6,
"interval_days":0,
"interval_months":0,
"max_value":0,
"not_exact":false,
"start_days":0,
"start_months":0,
"value":0
},
"payment_once":false,
"price":9.07
}
Supported methods
<base_url>/v2/contracts?member_id=202202022
{
"contracts": [{...contract data...}]
}
{
"id": 123,
"name": "Mitgliedschaft Plus",
"member_id": 202200022,
"contract_type_id": 132,
"start": "2020-01-01T00:00:00",
"end": "2021-01-01T00:00:00",
"payment_start": "2020-02-01T00:00:00",
"active": true,
"cancelled": "2024-12-12",
"notice_period": "2024-12-31",
"sepa_iban": "DE89370400440532013000",
"sepa_bic": "COBADEFFXXX",
"sepa_kto": "1234567890",
"sepa_blz": 37040044,
"adv_member_id": 202200023,
"adv_member_text": "Some text",
"ba_street": "teststreet",
"ba_nr": "123",
"ba_surname": "testname",
"ba_name": "testfirstname",
"ba_phone": "123456789",
"ba_mobile": "123456789",
"ba_email": "test@test.test"
}
(Development)
to be done
(Development)
(Released)
Supported methods
{
"id":96,
"sign": "SG",
"name":"Sales Gespräch",
"gym_id": 2
}
Supported methods
Additional query parameters:
{
"begin":"2024-05-31T16:00:00",
"end":"2024-05-31T16:30:00",
"price":"0,00",
"product_id":0,
"room_id":3,
"type_id":96,
"user_id":3
}
Supported methods
{
"type_id": 96,
"member_id": 201901069,
"user_id": 3,
"creator_id": 3,
"begin": "2024-05-31T14:30:00",
"end": "2024-05-31T15:00:00",
"room_id": 3
}
GET
/emailVerify
Supported methods
In this case this has to be the root object.
{
"email": "..."
}
Result:
{
"email": "...",
"emailVerify": {
"email_found": true,
"login_available": false,
"member_id": 202202022
}
}
curl Example:
curl --request POST --url 'https://api.aidoo-online.de:10015/v2/emailVerify' --header "Content-Type: application/json" -d '{"email": "XXX-EMAIL-XXX"}' --header 'Authorization: Bearer XXX-KEY-XXX'
The API supports registering an observer with a webhook to changes occuring to the supported entities. This features is highly customizable as well: You can set filters and extensions for the entity such that your changes are filtered by the filters and extended by the extensions.
Supported methods
{
"cached":true,
"entity_name":"contract",
"interval_seconds":10,
"observer_name":"my_amazing_company",
"webhook":{
"api_gym_id":"",
"api_key":"Bearer xxxxxxxxx",
"api_name":"test_webhook",
"api_url":"https://webhook.site/2205cb66-d9d7-4e9b-b52b-18625b61ccdc",
"header_field":"Authorization"
}
}
After the webhook is created, it may take up to 1 Minute before it is active. Afterwards, changes fitting the observers criteria are dispatched to the observer endpoint using POST:
{
"change_type": "create",
"changed_key": "10961",
"entity_data": {
"active": false,
"adv_member_id": 0,
"adv_member_text": "",
"contract_type_id": 312,
"end": "2025-02-28",
"id": 10961,
"member_id": 202200267,
"name": "00JensFitnessvertrag",
"notice_period": "2025-01-31",
"payment_start": "2024-03-01",
"start": "2024-03-01"
},
"entity_name": "contract"
}