This is a fuller version of SKILL.md for users running the install themselves without an agent. If you're using an AI agent to install, skip this — the agent will read SKILL.md and walk you through.
- A Tesla you own (2021+ for full Fleet API support including TVCP control commands)
- Tesla account credentials (the one in your Tesla mobile app)
- A public domain you control
- Python 3.11+
- Go 1.22+ (only needed for control commands — Phase 9)
-
Go to https://developer.tesla.com (or https://developer.tesla.cn for China-region vehicles)
-
Sign in with your Tesla owner account
-
Register Application with these fields:
Field Value Application Name (anything, e.g. "Tesla Skill for Alex") Description "Personal Tesla control via AI agents" Purpose Personal Use Allowed Origin URL https://<your-domain>(no path, no trailing slash)Allowed Redirect URI https://<your-domain>/oauth/callbackPrivacy Policy URL https://<your-domain>/privacy.htmlOAuth Grant Type "Authorization Code and Machine to Machine" Scopes openidoffline_accessvehicle_device_datavehicle_cmdsvehicle_charging_cmds -
Submit → wait 1–3 business days for review
car.<your-domain>, auto.<your-domain>, garage.<your-domain>, etc.
- Once approved, copy from the developer console:
- Client ID (public)
- Client Secret (treat like a password — never commit, never share)
git clone https://github.com/Vibetool/tesla-skill.git
cd tesla-skill
python3 -m venv .venv
source .venv/bin/activate
pip install -e .cp .env.example .envEdit .env:
USE_MOCK=false
TESLA_CLIENT_ID=<from Phase 1>
TESLA_CLIENT_SECRET=<from Phase 1>
TESLA_REDIRECT_URI=https://<your-domain>/oauth/callback
# Region — pick one:
# China: TESLA_FLEET_API_BASE=https://fleet-api.prd.cn.vn.cloud.tesla.cn
# TESLA_AUTH_BASE=https://auth.tesla.cn
# Global: TESLA_FLEET_API_BASE=https://fleet-api.prd.na.vn.cloud.tesla.com
# TESLA_AUTH_BASE=https://auth.tesla.com
TOKEN_ENCRYPTION_KEY=<generate one>Generate a Fernet key:
python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"python scripts/generate_virtual_key.pyTwo files appear:
~/.tesla-skill/tesla_keys/private.pem— secret, never share./public/.well-known/appspecific/com.tesla.3p.public-key.pem— public
The public key must be served at https://<your-domain>/.well-known/appspecific/com.tesla.3p.public-key.pem.
Copy the file to your webroot and ensure your web server doesn't block .well-known:
# In your nginx server block, BEFORE any catch-all locations:
location ^~ /.well-known/ {
root /var/www/<your-domain>;
}Verify:
curl -I https://<your-domain>/.well-known/appspecific/com.tesla.3p.public-key.pem
# Expect: HTTP/2 200- Create a static repo
- Place file at
<repo>/public/.well-known/appspecific/com.tesla.3p.public-key.pem(or<repo>/.well-known/appspecific/...depending on the host's webroot convention) - Configure custom domain (
<your-domain>) - Verify the URL returns 200
Also place a small privacy.html at the domain root (Tesla's auditor crawls this):
<!DOCTYPE html>
<html><head><meta charset="UTF-8"><title>Privacy Policy</title></head>
<body style="font-family:sans-serif;max-width:720px;margin:60px auto;padding:0 20px">
<h1>Privacy Policy</h1>
<p>This personal application uses Tesla's Fleet API to read state and send commands
to vehicles owned by the authenticated user. No data is shared with third parties.</p>
<p>Tokens are stored encrypted on the server and used only to make API calls on the
user's behalf. The user can revoke access at any time via the Tesla mobile app
(Account → Authorized Apps).</p>
<p>Contact: privacy@<your-domain></p>
</body></html>Once the public key URL returns 200:
python scripts/register_partner.pyThis is one-shot — only re-run if you change domains or rotate the keypair.
Two paths:
Run python -m tesla_skill.auth.callback_server on your server (listens on 127.0.0.1:8001 by default), behind nginx that proxies /oauth/* to it. Then visit:
https://<your-domain>/oauth/authorize
Add http://localhost:8765/callback as an additional Redirect URI in your developer portal app config, then:
python -m tesla_skill.auth.authorize
# (or: tesla-skill-authorize)This opens your browser, catches the redirect on http://localhost:8765, exchanges the code, and saves encrypted tokens to ~/.tesla-skill/tokens.db.
The car needs to trust your public key. This is a one-time BLE pairing via the Tesla mobile app, in person, near the car.
- Open Tesla app on your phone (logged in as the car's owner)
- Walk to within ~5m of the car
- Open this URL in your phone's system browser (not in-app browser):
(Use
https://tesla.com/_ak/<your-domain>https://www.tesla.cn/_ak/<your-domain>for China-region cars.) - Phone hands off to Tesla app → "Add Virtual Key from ?"
- Tap Add, enter your Tesla PIN (the 4-digit pin-to-drive code, NOT account password)
- App fetches public key from your domain, writes it to the car over BLE
Verify: Tesla app → Vehicle → Security → Keys should now list a third-party entry.
Reading status works without this. Adding control commands requires Tesla's official Go CLI:
# Install Go (1.22+)
# macOS: brew install go
# Linux: https://go.dev/dl/
# Build
git clone https://github.com/teslamotors/vehicle-command.git /tmp/vehicle-command
cd /tmp/vehicle-command
go build -o tesla-control ./cmd/tesla-control
# Install
mkdir -p "$HOME/.tesla-skill/bin"
mv tesla-control "$HOME/.tesla-skill/bin/"
chmod +x "$HOME/.tesla-skill/bin/tesla-control"Verify with a safe smoke test (just blinks lights):
TOKEN=$(python -c "from tesla_skill.auth.oauth import get_valid_access_token; print(get_valid_access_token())")
echo "$TOKEN" > /tmp/tt.token
chmod 600 /tmp/tt.token
~/.tesla-skill/bin/tesla-control \
-key-file ~/.tesla-skill/tesla_keys/private.pem \
-vin <YOUR_VIN> \
-token-file /tmp/tt.token \
flash-lights
# Lights should flash if your car is awake nearby
rm /tmp/tt.tokenSee AGENTS.md.
| Symptom | Cause | Fix |
|---|---|---|
403 Forbidden on /vehicle_data |
Virtual Key not paired (or pairing not yet propagated) | Phase 8, then wait 5–10 min |
register_partner.py says public key not accessible |
nginx blocks .well-known/; or domain not propagated |
Add location ^~ /.well-known/; check dig |
set_climate returns "tesla-control not found" |
TESLA_CONTROL_BINARY path wrong |
chmod +x the binary, fix .env |
vehicle_data?endpoints=... returns 403 with %3B in URL |
httpx URL-encoded the ; separators |
(Already fixed in real.py. If you see this on a fork, build URL with literal ;.) |
OAuth says invalid_redirect_uri |
Mismatch between Tesla portal config and .env |
Must be character-for-character identical (slashes count) |
| Token always invalid after a few days | refresh_token expired (Tesla expires tokens unused for ~3 months) | Re-run authorize flow |
Repeated asleep even after wake |
Car in deep sleep, can't reach LTE | Open Tesla app on phone, view vehicle to force wake; or wait |
tesla_keys/private.pemis your signing key —chmod 600on Linux, never commit, never paste in chat- If your server is compromised, immediately revoke the third-party key from the Tesla mobile app → Vehicle → Security → Keys → tap the entry → Remove. Even with the private key, an attacker can't sign commands for your car after revocation.
tokens.dbcontains encrypted refresh_token — needs the Fernet key in your.envto decrypt