├── .gitattributes ├── README.md ├── Tesla API.postman_collection.json ├── Tesla Environment.postman_environment.json └── images ├── TeslaLogIn-A.jpg ├── TeslaLogIn-B.jpg ├── TeslaLogIn-C.jpg ├── pmManageTokenDlgUpdated.jpg ├── pmManageTokenNav.jpg └── pmTokenRequestBottom.jpg /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Using the Tesla API with Desktop Version of Postman 2 | 3 | **PLEASE READ STEPS DOCUMENTATION BEFORE ATTEMPTING AUTHENTICATION.** Familarity with [Postman](https://www.postman.com/) is recommended. Postman has encapsulated the OAuth 2.0 sequence reducing the number of steps from the previous release of this collection. 4 | 5 | It is not necessary to store your credentials in Postman. Postman presents the Tesla authentication web page from Tesla's website in an embedded browser to generate the access token. 6 | 7 | Thanks to Eric Schnabel for the first update that included this method, thanks to Postman for this [reference](https://documenter.getpostman.com/view/5857899/TW74k5k4), and thanks to tesla-info for this [reference](https://tesla-info.com/tesla-token.php)! 8 | 9 | **Note: This collection does not function as described on the web version of Postman.** 10 | 11 | ### Step 1: Request Tesla Access Token 12 | 13 | USER PREPARATION: Enter the client secret in the environment. 14 | This Variable is already defined in the imported "Tesla environment" of Postman. 15 | Update the defined "clientsecret" variable which is [available here](https://pastebin.com/pS7Z6yyP). 16 | The client ID is not needed. Save these changes before hitting the button to request a token. 17 | 18 | #### Documentation 19 | 20 | Navigate to the Authorization tab of the "Request Tesla Access Token" call. Scroll to the bottom and click on "Get New Access Token" . Postman presents the Tesla authentication web page from the Tesla website. Enter your credentials. If MFA is enabled, it will request a code. 21 | 22 | ##### Log In Sequence 23 | 24 | The Tesla Log In Sequence collects your email, password, and MFA code (if enabled). 25 | 26 | ##### Email 27 | 28 | 29 | ##### Password 30 | 31 | 32 | ##### MFA (if enabled) 33 | 34 | 35 | 36 | When the authtication completes, the access token and refresh token are placed into the Token Manager 37 | 38 | and named with the most recent date and time . 39 | 40 | To use the tokens, they must be copied and pasted into the environment. Presently, this is a manual task; Postman may provide some automation in the future. Carefully copy the access token to the access_token environment variable, and copy the refresh token to the refresh_token environment variable. 41 | 42 | Note the access token is valid for eight hours. 43 | 44 | ### Step 2: Refresh Tesla Access Token 45 | 46 | #### Documentation 47 | 48 | The purpose of this step is refresh the access token, update the requestState environment variable, and to initialize an environment variable that contains the expiration date and time (readableValidUntilDate). The value of readableValidUntilDate is presented in the Console to show the expiration date and time of the access token. You DO NOT have to wait for an access token to expire before using Refresh Tesla Access Token. 49 | 50 | Click on Send to refresh the access token. Note that the refresh token is valid for 60 days. After the refresh token expires, it is necessary to use Request Tesla Access Token to create valid access and refresh tokens (I have not validated the 60 day claim). 51 | 52 | ### API Calls 53 | 54 | #### Get Vehicles 55 | 56 | NOTE: Use Get Vehicles first. 57 | 58 | The Get Vehicles call retrieves a list of vehicles on your Tesla account. It extracts the id_s variable for the first vehicle and stores it in the teslaVehicleIdS environment variable. Without this value, other API calls will fail. Once id_s has been retrieved, Get Vehicles is not needed for the API calls. 59 | 60 | #### Wake Vehicle 61 | 62 | The Wake Vehicle call wakes a sleeping vehicle. A vehicle must be online to respond to requests for vehicle information. 63 | 64 | The Tests contains a check named Vehicle On-Line Check. If the test passes, the vehicle is online. The access token expiration date and time appear in the Console. 65 | 66 | #### Get Vehicle Data 67 | 68 | The Get Vehicle Data call retrieves all available data for the vehicle defined by the teslaVehicleIdS environment variable. 69 | 70 | #### Get Drive State 71 | 72 | The Get Drive State call retrieves drive state data (location, power, shift state, and other data) for the vehicle defined by the teslaVehicleIdS environment variable. 73 | 74 | #### Get Charge State 75 | 76 | The Get Charge State call retrieves charge state data (battery level, charging information, and other data) for the vehicle defined by the teslaVehicleIdS environment variable. 77 | -------------------------------------------------------------------------------- /Tesla API.postman_collection.json: -------------------------------------------------------------------------------- 1 | { 2 | "info": { 3 | "_postman_id": "57f08d62-cfa1-4ae3-90a2-047b65328dda", 4 | "name": "Tesla API", 5 | "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" 6 | }, 7 | "item": [ 8 | { 9 | "name": "Request Tesla Access Token", 10 | "event": [ 11 | { 12 | "listen": "test", 13 | "script": { 14 | "exec": [ 15 | "" 16 | ], 17 | "type": "text/javascript" 18 | } 19 | }, 20 | { 21 | "listen": "prerequest", 22 | "script": { 23 | "exec": [ 24 | "" 25 | ], 26 | "type": "text/javascript" 27 | } 28 | } 29 | ], 30 | "protocolProfileBehavior": { 31 | "disableBodyPruning": true 32 | }, 33 | "request": { 34 | "auth": { 35 | "type": "oauth2", 36 | "oauth2": [ 37 | { 38 | "key": "state", 39 | "value": "{{requestState}}", 40 | "type": "string" 41 | }, 42 | { 43 | "key": "tokenName", 44 | "value": "teslaToken_{{$isoTimestamp}}", 45 | "type": "string" 46 | }, 47 | { 48 | "key": "clientId", 49 | "value": "ownerapi", 50 | "type": "string" 51 | }, 52 | { 53 | "key": "grant_type", 54 | "value": "authorization_code_with_pkce", 55 | "type": "string" 56 | }, 57 | { 58 | "key": "client_authentication", 59 | "value": "body", 60 | "type": "string" 61 | }, 62 | { 63 | "key": "accessTokenUrl", 64 | "value": "{{tokenEndPoint}}token", 65 | "type": "string" 66 | }, 67 | { 68 | "key": "authUrl", 69 | "value": "{{tokenEndPoint}}authorize", 70 | "type": "string" 71 | }, 72 | { 73 | "key": "clientSecret", 74 | "value": "{{clientsecret}}", 75 | "type": "string" 76 | }, 77 | { 78 | "key": "scope", 79 | "value": "openid email offline_access", 80 | "type": "string" 81 | }, 82 | { 83 | "key": "redirect_uri", 84 | "value": "https://auth.tesla.com/void/callback", 85 | "type": "string" 86 | }, 87 | { 88 | "key": "addTokenTo", 89 | "value": "header", 90 | "type": "string" 91 | } 92 | ] 93 | }, 94 | "method": "GET", 95 | "header": [], 96 | "body": { 97 | "mode": "raw", 98 | "raw": "", 99 | "options": { 100 | "raw": { 101 | "language": "json" 102 | } 103 | } 104 | }, 105 | "url": { 106 | "raw": "" 107 | } 108 | }, 109 | "response": [] 110 | }, 111 | { 112 | "name": "Wake Vehicle", 113 | "event": [ 114 | { 115 | "listen": "test", 116 | "script": { 117 | "exec": [ 118 | " var isValid = true;\r", 119 | " var todaysDate = new Date();\r", 120 | "\r", 121 | " pm.test(\"Vehicle On-Line Check\", function () {\r", 122 | " var jsonData = pm.response.json();\r", 123 | " pm.expect(jsonData.response.state).to.eql('online');\r", 124 | " });\r", 125 | "\r", 126 | " pm.test(\"Valid Token Check\", function () {\r", 127 | " if (todaysDate.getTime() > pm.environment.get(\"tokenValidUntil\")) {\r", 128 | " console.log(\"Token has expired.\");\r", 129 | " isValid = false;\r", 130 | " }\r", 131 | " else {\r", 132 | " console.log(\"Token valid until: \" + pm.environment.get(\"readableValidUntilDate\"));\r", 133 | " }\r", 134 | " pm.expect(isValid).to.eql(true);\r", 135 | " });" 136 | ], 137 | "type": "text/javascript" 138 | } 139 | }, 140 | { 141 | "listen": "prerequest", 142 | "script": { 143 | "exec": [ 144 | "" 145 | ], 146 | "type": "text/javascript" 147 | } 148 | } 149 | ], 150 | "protocolProfileBehavior": { 151 | "disabledSystemHeaders": {} 152 | }, 153 | "request": { 154 | "auth": { 155 | "type": "bearer", 156 | "bearer": [ 157 | { 158 | "key": "token", 159 | "value": "{{access_token}}", 160 | "type": "string" 161 | } 162 | ] 163 | }, 164 | "method": "POST", 165 | "header": [], 166 | "url": { 167 | "raw": "{{endPoint}}/api/1/vehicles/{{teslaVehicleIdS}}/wake_up", 168 | "host": [ 169 | "{{endPoint}}" 170 | ], 171 | "path": [ 172 | "api", 173 | "1", 174 | "vehicles", 175 | "{{teslaVehicleIdS}}", 176 | "wake_up" 177 | ] 178 | } 179 | }, 180 | "response": [] 181 | }, 182 | { 183 | "name": "Get Vehicle Data", 184 | "event": [ 185 | { 186 | "listen": "test", 187 | "script": { 188 | "exec": [ 189 | "var isValid = true;\r", 190 | "var todaysDate = new Date();\r", 191 | "\r", 192 | "var jsonData = pm.response.json();\r", 193 | "\r", 194 | "if (jsonData.response.charge_state.charging_state == \"Complete\" || jsonData.response.charge_state.charging_state == \"Stopped\") {\r", 195 | " console.log(\"Charing State: \" + jsonData.response.charge_state.charging_state + \", \" + \"Charge Data: \" + jsonData.response.charge_state.charge_energy_added + \", Odometer: \" + jsonData.response.vehicle_state.odometer + \", battery range: \" + jsonData.response.charge_state.battery_range + \", estimated range: \" + jsonData.response.charge_state.est_battery_range + \", ideal range: \" + jsonData.response.charge_state.ideal_battery_range + \", outside temp: \" + jsonData.response.climate_state.outside_temp);\r", 196 | "} else {\r", 197 | " console.log(\"Still charging. Time to full charge: \" + jsonData.response.charge_state.time_to_full_charge);\r", 198 | "}\r", 199 | "\r", 200 | "pm.test(\"Valid Token Check\", function () {\r", 201 | " if (todaysDate.getTime() > pm.environment.get(\"tokenValidUntil\")) {\r", 202 | " console.log(\"Token has expired.\");\r", 203 | " isValid = false;\r", 204 | " }\r", 205 | " else {\r", 206 | " console.log(\"Token valid until: \" + pm.environment.get(\"readableValidUntilDate\"));\r", 207 | " }\r", 208 | " pm.expect(isValid).to.eql(true);\r", 209 | " });\r", 210 | "\r", 211 | "" 212 | ], 213 | "type": "text/javascript" 214 | } 215 | }, 216 | { 217 | "listen": "prerequest", 218 | "script": { 219 | "exec": [ 220 | "" 221 | ], 222 | "type": "text/javascript" 223 | } 224 | } 225 | ], 226 | "request": { 227 | "auth": { 228 | "type": "bearer", 229 | "bearer": [ 230 | { 231 | "key": "token", 232 | "value": "{{access_token}}", 233 | "type": "string" 234 | } 235 | ] 236 | }, 237 | "method": "GET", 238 | "header": [], 239 | "url": { 240 | "raw": "{{endPoint}}/api/1/vehicles/{{teslaVehicleIdS}}/vehicle_data", 241 | "host": [ 242 | "{{endPoint}}" 243 | ], 244 | "path": [ 245 | "api", 246 | "1", 247 | "vehicles", 248 | "{{teslaVehicleIdS}}", 249 | "vehicle_data" 250 | ] 251 | } 252 | }, 253 | "response": [] 254 | }, 255 | { 256 | "name": "Get Vehicles", 257 | "event": [ 258 | { 259 | "listen": "test", 260 | "script": { 261 | "exec": [ 262 | "var isValid = true;\r", 263 | "var todaysDate = new Date();\r", 264 | "\r", 265 | "var jsonData = pm.response.json();\r", 266 | "var vehicleIdS = jsonData.response[0].id_s;\r", 267 | "//set environment variable for use in all other APIs \r", 268 | "pm.environment.set(\"teslaVehicleIdS\", vehicleIdS);\r", 269 | "\r", 270 | "pm.test(\"Valid Token Check\", function () {\r", 271 | " if (todaysDate.getTime() > pm.environment.get(\"tokenValidUntil\")) {\r", 272 | " console.log(\"Token has expired.\");\r", 273 | " isValid = false;\r", 274 | " }\r", 275 | " else {\r", 276 | " console.log(\"Token valid until: \" + pm.environment.get(\"readableValidUntilDate\"));\r", 277 | " }\r", 278 | " pm.expect(isValid).to.eql(true);\r", 279 | " });\r", 280 | "\r", 281 | "\r", 282 | "" 283 | ], 284 | "type": "text/javascript" 285 | } 286 | } 287 | ], 288 | "request": { 289 | "auth": { 290 | "type": "bearer", 291 | "bearer": [ 292 | { 293 | "key": "token", 294 | "value": "{{access_token}}", 295 | "type": "string" 296 | } 297 | ] 298 | }, 299 | "method": "GET", 300 | "header": [], 301 | "url": { 302 | "raw": "{{endPoint}}api/1/vehicles", 303 | "host": [ 304 | "{{endPoint}}api" 305 | ], 306 | "path": [ 307 | "1", 308 | "vehicles" 309 | ] 310 | } 311 | }, 312 | "response": [] 313 | }, 314 | { 315 | "name": "Get Drive State", 316 | "event": [ 317 | { 318 | "listen": "test", 319 | "script": { 320 | "exec": [ 321 | "var isValid = true;\r", 322 | "var todaysDate = new Date();\r", 323 | "\r", 324 | "pm.test(\"Valid Token Check\", function () {\r", 325 | " if (todaysDate.getTime() > pm.environment.get(\"tokenValidUntil\")) {\r", 326 | " console.log(\"Token has expired.\");\r", 327 | " isValid = false;\r", 328 | " }\r", 329 | " else {\r", 330 | " console.log(\"Token valid until: \" + pm.environment.get(\"readableValidUntilDate\"));\r", 331 | " }\r", 332 | " pm.expect(isValid).to.eql(true);\r", 333 | " });\r", 334 | "\r", 335 | "\r", 336 | "" 337 | ], 338 | "type": "text/javascript" 339 | } 340 | } 341 | ], 342 | "request": { 343 | "auth": { 344 | "type": "bearer", 345 | "bearer": [ 346 | { 347 | "key": "token", 348 | "value": "{{access_token}}", 349 | "type": "string" 350 | } 351 | ] 352 | }, 353 | "method": "GET", 354 | "header": [], 355 | "url": { 356 | "raw": "{{endPoint}}/api/1/vehicles/{{teslaVehicleIdS}}/data_request/drive_state", 357 | "host": [ 358 | "{{endPoint}}" 359 | ], 360 | "path": [ 361 | "api", 362 | "1", 363 | "vehicles", 364 | "{{teslaVehicleIdS}}", 365 | "data_request", 366 | "drive_state" 367 | ] 368 | } 369 | }, 370 | "response": [] 371 | }, 372 | { 373 | "name": "Get Charge State", 374 | "event": [ 375 | { 376 | "listen": "test", 377 | "script": { 378 | "exec": [ 379 | "var isValid = true;\r", 380 | "var todaysDate = new Date();\r", 381 | "\r", 382 | "pm.test(\"Valid Token Check\", function () {\r", 383 | " if (todaysDate.getTime() > pm.environment.get(\"tokenValidUntil\")) {\r", 384 | " console.log(\"Token has expired.\");\r", 385 | " isValid = false;\r", 386 | " }\r", 387 | " else {\r", 388 | " console.log(\"Token valid until: \" + pm.environment.get(\"readableValidUntilDate\"));\r", 389 | " }\r", 390 | " pm.expect(isValid).to.eql(true);\r", 391 | " });\r", 392 | "\r", 393 | "\r", 394 | "" 395 | ], 396 | "type": "text/javascript" 397 | } 398 | } 399 | ], 400 | "request": { 401 | "auth": { 402 | "type": "bearer", 403 | "bearer": [ 404 | { 405 | "key": "token", 406 | "value": "{{access_token}}", 407 | "type": "string" 408 | } 409 | ] 410 | }, 411 | "method": "GET", 412 | "header": [], 413 | "url": { 414 | "raw": "{{endPoint}}api/1/vehicles/{{teslaVehicleIdS}}/data_request/charge_state", 415 | "host": [ 416 | "{{endPoint}}api" 417 | ], 418 | "path": [ 419 | "1", 420 | "vehicles", 421 | "{{teslaVehicleIdS}}", 422 | "data_request", 423 | "charge_state" 424 | ] 425 | } 426 | }, 427 | "response": [] 428 | }, 429 | { 430 | "name": "Refresh Tesla Access Token", 431 | "event": [ 432 | { 433 | "listen": "test", 434 | "script": { 435 | "exec": [ 436 | "var MonthAbbr = [\"Jan\",\"Feb\",\"Mar\",\"Apr\",\"May\",\"Jun\",\"Jul\",\"Aug\",\"Sept\",\"Oct\",\"Nov\",\"Dec\"];\r", 437 | "const characters ='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';\r", 438 | "var c_s;\r", 439 | "\r", 440 | "function generateString(length) {\r", 441 | " let result = '';\r", 442 | " const charactersLength = characters.length;\r", 443 | " for ( let i = 0; i < length; i++ ) {\r", 444 | " result += characters.charAt(Math.floor(Math.random() * charactersLength));\r", 445 | " }\r", 446 | "\r", 447 | " return result;\r", 448 | "}\r", 449 | "\r", 450 | "var todaysDate = new Date();\r", 451 | "\r", 452 | "var jsonData = pm.response.json();\r", 453 | "pm.environment.set(\"access_token\",jsonData.access_token);\r", 454 | "\r", 455 | "var expiresIn = jsonData.expires_in;\r", 456 | "var tokenValidUntil = todaysDate.setTime(todaysDate.getTime() + (expiresIn * 1000));\r", 457 | "pm.environment.set(\"tokenValidUntil\",tokenValidUntil);\r", 458 | "\r", 459 | "var validUntilDate = new Date(tokenValidUntil);\r", 460 | "var readableValidUntilDate = validUntilDate.getDate().toString().padStart(2,\"0\") + \r", 461 | "\" \" + MonthAbbr[validUntilDate.getMonth()] + \r", 462 | "\" \" + validUntilDate.getFullYear() + \r", 463 | "\" \" + validUntilDate.getHours().toString().padStart(2,\"0\") + \r", 464 | "\":\" + validUntilDate.getMinutes().toString().padStart(2,\"0\") + \r", 465 | "\":\" + validUntilDate.getSeconds().toString().padStart(2,\"0\");\r", 466 | "pm.environment.set(\"readableValidUntilDate\",readableValidUntilDate);\r", 467 | "\r", 468 | "console.log (\"valid until: \" + readableValidUntilDate);\r", 469 | "\r", 470 | "//update requestState\r", 471 | "c_s = generateString(16);\r", 472 | "pm.environment.set(\"requestState\",c_s);" 473 | ], 474 | "type": "text/javascript" 475 | } 476 | } 477 | ], 478 | "request": { 479 | "method": "POST", 480 | "header": [], 481 | "body": { 482 | "mode": "raw", 483 | "raw": "{\r\n \"grant_type\": \"refresh_token\",\r\n \"refresh_token\": \"{{refresh_token}}\",\r\n \"client_id\": \"ownerapi\",\r\n \"client_secret\": \"{{client_secret}}\"\r\n}", 484 | "options": { 485 | "raw": { 486 | "language": "json" 487 | } 488 | } 489 | }, 490 | "url": { 491 | "raw": "{{tokenEndPoint}}token", 492 | "host": [ 493 | "{{tokenEndPoint}}token" 494 | ] 495 | } 496 | }, 497 | "response": [] 498 | } 499 | ] 500 | } -------------------------------------------------------------------------------- /Tesla Environment.postman_environment.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "bbe0defb-0de1-489f-82a1-5262b74aa853", 3 | "name": "Tesla Environment", 4 | "values": [ 5 | { 6 | "key": "endPoint", 7 | "value": "https://owner-api.teslamotors.com/", 8 | "enabled": true 9 | }, 10 | { 11 | "key": "tokenEndPoint", 12 | "value": "https://auth.tesla.com/oauth2/v3/", 13 | "enabled": true 14 | }, 15 | { 16 | "key": "access_token", 17 | "value": "", 18 | "enabled": true 19 | }, 20 | { 21 | "key": "refresh_token", 22 | "value": "", 23 | "enabled": true 24 | }, 25 | { 26 | "key": "clientsecret", 27 | "value": "", 28 | "type": "default", 29 | "enabled": true 30 | }, 31 | { 32 | "key": "teslaVehicleIdS", 33 | "value": "", 34 | "enabled": true 35 | }, 36 | { 37 | "key": "tokenValidUntil", 38 | "value": "", 39 | "type": "default", 40 | "enabled": true 41 | }, 42 | { 43 | "key": "readableValidUntilDate", 44 | "value": "", 45 | "type": "default", 46 | "enabled": true 47 | }, 48 | { 49 | "key": "requestState", 50 | "value": "VFvXQTjUbfdaF2iQ", 51 | "type": "default", 52 | "enabled": true 53 | } 54 | ], 55 | "_postman_variable_scope": "environment", 56 | "_postman_exported_at": "2022-05-01T23:30:14.356Z", 57 | "_postman_exported_using": "Postman/9.16.0" 58 | } -------------------------------------------------------------------------------- /images/TeslaLogIn-A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jdemeyer1/TeslaAPIThruPostman/4859bb763d2c2857089af7359f9f58eef954cecb/images/TeslaLogIn-A.jpg -------------------------------------------------------------------------------- /images/TeslaLogIn-B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jdemeyer1/TeslaAPIThruPostman/4859bb763d2c2857089af7359f9f58eef954cecb/images/TeslaLogIn-B.jpg -------------------------------------------------------------------------------- /images/TeslaLogIn-C.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jdemeyer1/TeslaAPIThruPostman/4859bb763d2c2857089af7359f9f58eef954cecb/images/TeslaLogIn-C.jpg -------------------------------------------------------------------------------- /images/pmManageTokenDlgUpdated.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jdemeyer1/TeslaAPIThruPostman/4859bb763d2c2857089af7359f9f58eef954cecb/images/pmManageTokenDlgUpdated.jpg -------------------------------------------------------------------------------- /images/pmManageTokenNav.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jdemeyer1/TeslaAPIThruPostman/4859bb763d2c2857089af7359f9f58eef954cecb/images/pmManageTokenNav.jpg -------------------------------------------------------------------------------- /images/pmTokenRequestBottom.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jdemeyer1/TeslaAPIThruPostman/4859bb763d2c2857089af7359f9f58eef954cecb/images/pmTokenRequestBottom.jpg --------------------------------------------------------------------------------