Skip to main content
How do I authenticate to access the REST API?

A guide to help you Migrate to the Device Code Flow

tado° avatar
Written by tado°
Updated over a week ago

Note: This article was intended for hobbyists and open-source projects to grant access to the API without any additional support from our technical team.

To enhance the customer experience and improve the login process in the future, we are busy upgrading our user management system. As part of this update, we need to remove access to the "password grant flow", which many users currently use to obtain tokens for the tado° API.

Instead, we are enabling the "device code grant flow", which is a more secure process to obtain tokens. Read here for general information on this.

To make the switch, please do the following (code snippets are shown in Python):

  1. Initiate the device code flow by "posting" to the following endpoint. If you want this process to return a refresh token (to be used to regularly get new access tokens), then you need to add "offline_access" to the requested scope.

    req = requests.post(
    "https://login.tado.com/oauth2/device_authorize",
    params=dict(
    client_id="1bb50063-6b0c-4d11-bd99-387f4a91cc46",
    scope="offline_access",
    )
    )

  2. A successful call to this endpoint will return something that looks like:

    {'device_code': 'XXX_code_XXX',
    'expires_in': 300,
    'interval': 5,
    'user_code': '7BQ5ZQ',
    'verification_uri': 'https://login.tado.com/oauth2/device',
    'verification_uri_complete':'https://login.tado.com/oauth2/device?user_code=7BQ5ZQ'}

  3. At this point, you need to visit the site listed in "verification_uri_complete" in any browser (you can do this on another machine or your phone). On that site, the user code should autofill automatically, but it can be entered manually if needed. The process will be confirmed when you log in to your tado° account.

  4. Once the above site is visited and access is confirmed, a subsequent call can be made to complete the process and return an access token (plus a refresh token, in case "offline_access" was requested). Here, the "device_code" returned in the first call must be included in the request:

    req = requests.post(
    "https://login.tado.com/oauth2/token",
    params=dict(
    client_id="1bb50063-6b0c-4d11-bd99-387f4a91cc46",
    device_code="XXX_code_XXX",
    grant_type="urn:ietf:params:oauth:grant-type:device_code",
    )
    )

  5. Note: If step 3 is not successfully completed, the above request will return an error message stating this. The typical mechanism to deal with this is to slowly poll this endpoint (at an interval specified in the return of the first request). A successful request will return the following:

    {'access_token': 'myAccessToken',
    'expires_in': 3599,
    'refresh_token': 'myRefreshToken',
    'scope': 'offline_access',
    'token_type': 'Bearer',
    'userId': '...'}


    You can make any of your requests by passing the access token in the authorization header. The following is an example of a properly formed request:

    GET https://my.tado.com/api/v2/me 
    Authorization: Bearer myAccessToken

    Please note that this access token is valid for 10 minutes, so you should use the same token for multiple requests. This will make your integration run faster and prevent you from hitting any API rate limit on the authorization endpoint. Once your token expires you can simply obtain a new token through the normal refresh token flow, e.g.:

    token = requests.post(
    "https://login.tado.com/oauth2/token",
    params=dict(
    client_id="1bb50063-6b0c-4d11-bd99-387f4a91cc46",
    grant_type="refresh_token",
    refresh_token="myRefreshToken",
    ),
    ).json()

    This endpoint still uses refresh token rotation, meaning that the old refresh token is revoked and the new one is immediately valid. Refresh tokens are valid for up to 30 days, or until they are used in the refresh token flow.

Note that there are many open-source libraries that can help with managing standard OAuth flows/authentication, so you may be able to use one of these to simplify how you call our API.

Did this answer your question?