Skip to end of metadata
Go to start of metadata

Introduction

The Yona server API applies the REST architectural style and applies HATEOAS (Hypermedia As The Engine Of Application State). In simple words: The API follows the same pattern as regular web pages. The data can be retrieved with HTTP GET and updated with HTTP PUT. If it is possible to fetch related date or take an action on the data, then this is indicated through links in the data. The data is in JSON format and follows HAL (Hypertext Application Language).

API description

The Yona server API is described in Swagger (recently renamed into Open API Specification). Every instance of the Yona App Service comes with an embedded Swagger spec UI, e.g. https://app.prd.yona.nu/swagger-ui/index.html:

Example response

In this section, we look at the response of a GET on the user URL of Richard (see Test REST requests - Invite existing user as buddy) after he established a buddy relationship with Bob. That response looks like this:

{
  "firstName": "Richard",
  "lastName": "Quin",
  "mobileNumber": "+31612345678",
  "appLastOpenedDate": "2018-03-13",
  "nickname": "RQ",
  "yonaPassword": "AES:128:UIBPamj33m0jlzvW/v2o2g==",
  "creationTime": "2018-03-13T18:38:25.373+0000",
  "sslRootCertCN": "smoothwall003.yona",
  "_links": {
    "self": {
      "href": "https://app.prd.yona.nu/users/3cae5a18-3a7a-4edf-90a4-4a5250672c21?requestingUserId=3cae5a18-3a7a-4edf-90a4-4a5250672c21&requestingDeviceId=0567b2e2-3712-4b28-97f1-d128c6125010"
    },
    "yona:userPhoto": {
      "href": "https://app.prd.yona.nu/userPhotos/149652e1-754d-4b38-b4da-9c5532afc89f"
    },
    "edit": {
      "href": "https://app.prd.yona.nu/users/3cae5a18-3a7a-4edf-90a4-4a5250672c21?requestingDeviceId=0567b2e2-3712-4b28-97f1-d128c6125010"
    },
    "yona:messages": {
      "href": "https://app.prd.yona.nu/users/3cae5a18-3a7a-4edf-90a4-4a5250672c21/messages/"
    },
    "yona:dailyActivityReports": {
      "href": "https://app.prd.yona.nu/users/3cae5a18-3a7a-4edf-90a4-4a5250672c21/activity/days/"
    },
    "yona:weeklyActivityReports": {
      "href": "https://app.prd.yona.nu/users/3cae5a18-3a7a-4edf-90a4-4a5250672c21/activity/weeks/"
    },
    "yona:dailyActivityReportsWithBuddies": {
      "href": "https://app.prd.yona.nu/users/3cae5a18-3a7a-4edf-90a4-4a5250672c21/activity/withBuddies/days/?requestingDeviceId=0567b2e2-3712-4b28-97f1-d128c6125010"
    },
    "yona:newDeviceRequest": {
      "href": "https://app.prd.yona.nu/newDeviceRequests/+31612345678"
    },
    "yona:requestPinReset": {
      "href": "https://app.prd.yona.nu/users/3cae5a18-3a7a-4edf-90a4-4a5250672c21/pinResetRequest/request"
    },
    "yona:editUserPhoto": {
      "href": "https://app.prd.yona.nu/users/3cae5a18-3a7a-4edf-90a4-4a5250672c21/photo"
    },
    "curies": [
      {
        "href": "http://dev.yona.nu/rels/{rel}",
        "name": "yona",
        "templated": true
      }
    ]
  },
  "_embedded": {
    "yona:devices": {
      "_embedded": {
        "yona:devices": [
          {
            "name": "My S8",
            "operatingSystem": "ANDROID",
            "appLastOpenedDate": "2018-03-13",
            "vpnProfile": {
              "vpnPassword": "Jtkv5GmE7J3tdNTlDNVGSqcGfIZBwEFB",
              "vpnLoginID": "73770a6f-a26f-4e31-87c9-323ad905a082$0",
              "_links": {
                "yona:ovpnProfile": {
                  "href": "https://app.prd.yona.nu/vpn/profile.ovpn"
                }
              }
            },
            "sslRootCertCN": "smoothwall003.yona",
            "vpnConnected": true,
            "registrationTime": "2018-03-13T18:38:25.470+0000",
            "requestingDevice": true,
            "_links": {
              "self": {
                "href": "https://app.prd.yona.nu/users/3cae5a18-3a7a-4edf-90a4-4a5250672c21/devices/0567b2e2-3712-4b28-97f1-d128c6125010?requestingDeviceId=0567b2e2-3712-4b28-97f1-d128c6125010"
              },
              "edit": {
                "href": "https://app.prd.yona.nu/users/3cae5a18-3a7a-4edf-90a4-4a5250672c21/devices/0567b2e2-3712-4b28-97f1-d128c6125010?requestingDeviceId=0567b2e2-3712-4b28-97f1-d128c6125010"
              },
              "yona:postOpenAppEvent": {
                "href": "https://app.prd.yona.nu/users/3cae5a18-3a7a-4edf-90a4-4a5250672c21/devices/0567b2e2-3712-4b28-97f1-d128c6125010/openApp"
              },
              "yona:appActivity": {
                "href": "https://app.prd.yona.nu/users/3cae5a18-3a7a-4edf-90a4-4a5250672c21/devices/0567b2e2-3712-4b28-97f1-d128c6125010/appActivity/"
              },
              "yona:sslRootCert": {
                "href": "https://app.prd.yona.nu/ssl/rootcert.cer"
              },
              "yona:appleMobileConfig": {
                "href": "https://app.prd.yona.nu/users/3cae5a18-3a7a-4edf-90a4-4a5250672c21/devices/0567b2e2-3712-4b28-97f1-d128c6125010/apple.mobileconfig"
              }
            }
          }
        ]
      },
      "_links": {
        "self": {
          "href": "https://app.prd.yona.nu/users/3cae5a18-3a7a-4edf-90a4-4a5250672c21/devices/?requestingDeviceId=0567b2e2-3712-4b28-97f1-d128c6125010"
        }
      }
    },
    "yona:goals": {
      "_embedded": {
        "yona:goals": [
          {
            "creationTime": "2017-01-01T12:00:00.000+0000",
            "maxDurationMinutes": 0,
            "historyItem": false,
            "_links": {
              "self": {
                "href": "https://app.prd.yona.nu/users/3cae5a18-3a7a-4edf-90a4-4a5250672c21/goals/e6395085-d427-449b-a162-412c3788254d"
              },
              "yona:activityCategory": {
                "href": "https://app.prd.yona.nu/activityCategories/192d69f4-8d3e-499b-983c-36ca97340ba9"
              }
            },
            "@type": "BudgetGoal"
          }
        ]
      },
      "_links": {
        "self": {
          "href": "https://app.prd.yona.nu/users/3cae5a18-3a7a-4edf-90a4-4a5250672c21/goals/"
        }
      }
    },
    "yona:buddies": {
      "_embedded": {
        "yona:buddies": [
          {
            "nickname": "BD",
            "sendingStatus": "ACCEPTED",
            "receivingStatus": "ACCEPTED",
            "lastStatusChangeTime": "2018-03-13T18:46:22.410+0000",
            "_links": {
              "self": {
                "href": "https://app.prd.yona.nu/users/3cae5a18-3a7a-4edf-90a4-4a5250672c21/buddies/7e987807-d6df-4d1b-a129-09e789e51eb6"
              },
              "edit": {
                "href": "https://app.prd.yona.nu/users/3cae5a18-3a7a-4edf-90a4-4a5250672c21/buddies/7e987807-d6df-4d1b-a129-09e789e51eb6"
              },
              "yona:dailyActivityReports": {
                "href": "https://app.prd.yona.nu/users/3cae5a18-3a7a-4edf-90a4-4a5250672c21/buddies/7e987807-d6df-4d1b-a129-09e789e51eb6/activity/days/"
              },
              "yona:weeklyActivityReports": {
                "href": "https://app.prd.yona.nu/users/3cae5a18-3a7a-4edf-90a4-4a5250672c21/buddies/7e987807-d6df-4d1b-a129-09e789e51eb6/activity/weeks/"
              }
            },
            "_embedded": {
              "yona:user": {
                "firstName": "Bob",
                "lastName": "Dunn",
                "mobileNumber": "+31612345679",
                "appLastOpenedDate": "2018-03-13",
                "nickname": "BD",
                "creationTime": "2018-03-13T18:40:05.380+0000",
                "_links": {
                  "self": {
                    "href": "https://app.prd.yona.nu/users/ba12b1b0-9f2d-4650-a0f7-63ee441653c6?requestingUserId=3cae5a18-3a7a-4edf-90a4-4a5250672c21"
                  }
                },
                "_embedded": {
                  "yona:devices": {
                    "_embedded": {
                      "yona:devices": [
                        {
                          "name": "My iPhone",
                          "vpnConnected": true,
                          "requestingDevice": false,
                          "_links": {
                            "self": {
                              "href": "https://app.prd.yona.nu/users/ba12b1b0-9f2d-4650-a0f7-63ee441653c6/devices/ccdc588d-7c94-4cf3-8c60-de3fab8571bd{?requestingDeviceId}",
                              "templated": true
                            },
                            "edit": {
                              "href": "https://app.prd.yona.nu/users/ba12b1b0-9f2d-4650-a0f7-63ee441653c6/devices/ccdc588d-7c94-4cf3-8c60-de3fab8571bd{?requestingDeviceId}",
                              "templated": true
                            }
                          }
                        }
                      ]
                    },
                    "_links": {
                      "self": {
                        "href": "https://app.prd.yona.nu/users/ba12b1b0-9f2d-4650-a0f7-63ee441653c6/devices/{?requestingDeviceId}",
                        "templated": true
                      }
                    }
                  },
                  "yona:goals": {
                    "_embedded": {
                      "yona:goals": [
                        {
                          "creationTime": "2017-01-01T12:00:00.000+0000",
                          "maxDurationMinutes": 0,
                          "historyItem": false,
                          "_links": {
                            "self": {
                              "href": "https://app.prd.yona.nu/users/ba12b1b0-9f2d-4650-a0f7-63ee441653c6/goals/144785f0-13e2-44fd-abde-246cc236aa76"
                            },
                            "yona:activityCategory": {
                              "href": "https://app.prd.yona.nu/activityCategories/192d69f4-8d3e-499b-983c-36ca97340ba9"
                            }
                          },
                          "@type": "BudgetGoal"
                        }
                      ]
                    },
                    "_links": {
                      "self": {
                        "href": "https://app.prd.yona.nu/users/ba12b1b0-9f2d-4650-a0f7-63ee441653c6/goals/"
                      }
                    }
                  }
                }
              }
            }
          }
        ]
      },
      "_links": {
        "self": {
          "href": "https://app.prd.yona.nu/users/3cae5a18-3a7a-4edf-90a4-4a5250672c21/buddies/"
        }
      }
    }
  }
}

This response contains everything the app needs to remember for offline access.

LinesDescription
2 - 9The regular properties of the user
49 - 224

Embedded resources

LinesDescription
50 - 98

Embedded devices of this user

LinesDescription
53 - 90

The user's device named "My S8"

LinesDescription
54 - 69The regular properties of this device, including a nested structure for the VPN profile
72The URL of this device. It can be used to retrieve it again and as identification.
75The edit URL, through which this device can be renamed or deleted
77 - 88Other links specific to this device
95The URL of the devices collection of this user.
99 - 123

Embedded goals of this user

LinesDescription
102 - 115

The Gambling goal

LinesDescription
103 - 105The regular properties of this goal
108The URL of this goal. It can be used to retrieve it again and as identification.
114The type discriminator of this goal, see Polymorphism below
120The URL of the goals collection of this user. New goals can be created by POSTing them to this URL.
124 - 223

Embedded buddies of this user

LinesDescription
127 - 215

The buddy Bob

The data of the buddy and the related user, with all links are included, in accordance with the pattern described above for the device and goal.

11 - 48

Links to related resources or actions

LinesDescription
12The URL of this user. This URL needs to be stored in the app, as it provides the access to the user information
15The user photo URL. A GET on this will return a PNG image
18The edit and delete URL of this user. To update, do a PUT on this URL, or remove it through DELETE
21The URL of the messages of this user. A GET on this URL fetches a pageable set of the messages
24The URL with the daily activity reports of this user. A GET on this URL fetches a pageable set of the activity reports
27The URL with the weekly activity reports of this user. A GET on this URL fetches a pageable set of the activity reports
30The URL with the daily activity reports of this user and their buddies. A GET on this URL fetches a pageable set of the activity reports
33This URL must be used to POST a new device request
36If the user wants to reset their pin, the app must POST a pin reset request to this URL
39An updated user photo would need to be posted to this URL

The response contains a considerable set of URLs. The self-links are always there, but the other links are there if they are usable in the current state of the resource. E.g. before the mobile number of the user is confirmed, there won't be any links except for the the mobile number confirmation URL.

Polymorphism

In a few places, the server supports polymorphism: for the goals and the messages. There are two types of goals: budget goal and timezone goal. Both types of goals can be fetched, created and updated through the same URLs. The story for messages is similar: there is a large number of different types of messages. All of these are retrieved through the messages link of the user. The type of resource is indicated through the @type property.

Language awareness

The server returns error messages that might be visible to the user. To handle localization properly, every request needs to come with an Accept-Language header with the locale of the user, like this: Accept-Language: nl-NL. As only a limited set of languages is supported by the server, the content might not be in the language requested. This can be seen by looking at the Content-Language header.

Fixed URLs

Generally, the URLs should be taken from the responses delivered by the server. To set up an interaction with the server (i.e. before a response is received), the app needs to know a start URL. The following fixed start URLs exist:

  • /users/ – To POST the user sign up message
  • /activityCategories/ – To GET the list of activity categories
  • /newDeviceRequests/{mobileNumber} – To PUT, GET or DELETE a new device request
  • /admin/requestUserOverwrite/ – To POST an account overwrite request

These URLs are exceptions to the rule that the app should not build URLs. In all other cases, the URLs should be used that are returned in the responses from the server.

  • No labels