robotrace login
A browser-driven sign-in for the Python SDK. Instead of minting an
API key in the portal, copying the plaintext, and pasting it into
ROBOTRACE_API_KEY, you run one CLI command. The SDK takes care
of the round-trip and saves a credentials file your scripts pick up
automatically.
$ robotrace login
Welcome to RoboTrace (https://app.robotrace.dev).
Logging in this machine via your browser…
To authorize this device, open:
https://app.robotrace.dev/cli/auth?code=XKDF-PQ4N
Confirmation code: XKDF-PQ4N
Make sure the same code shows up on the page before you click Authorize.
Waiting for browser approval...
✓ Logged in as art@robotrace.dev.
Credentials saved to ~/.robotrace/credentials (profile: default).
Portal: https://app.robotrace.dev/portalThe next time you run a script that uses robotrace, no env vars
are needed:
import robotrace as rt
ep = rt.start_episode(name="pick_and_place v3")
# [robotrace] → https://app.robotrace.dev/portal/episodes/ep_8f3c
ep.upload_video("./run.mp4")
ep.finalize(status="ready")That [robotrace] → line is a clickable link in modern terminals
(iTerm2, the macOS Terminal, Alacritty, Wezterm, VS Code, JetBrains).
Cmd-click takes you straight to the new episode's portal page.
How it works
robotrace login implements a device-code flow modelled on
RFC 8628:
- The CLI hits
POST /api/cli/auth/startand gets back two tokens — adevice_code(its bearer secret) and auser_code(the human-readable confirmation string). - The CLI prints the verification URL with the
user_codeembedded, and tries to open your default browser there. You can pass--no-browserto skip the auto-open if you're SSH'd into a headless box and want to copy the URL by hand. - You sign in on the portal as usual, then land on
/cli/auth. The page shows the sameuser_codeplus the device fingerprint the CLI sent (your hostname, IP, user-agent). If it all checks out, you click Authorize device. - The portal mints a fresh API key for your account and stamps the approval onto the device session.
- The CLI's next poll picks up the key, writes it to
~/.robotrace/credentials, and exits.
The plaintext key sits in the database for at most a few seconds —
the polling endpoint nulls it the instant it hands it back, and the
session row's status moves to consumed so the same device_code
can never be redeemed twice.
What gets saved
# ~/.robotrace/credentials
# managed by `robotrace login` — do not commit.
[default]
api_key = "rt_…"
base_url = "https://app.robotrace.dev"
client_id = "<uuid>"
user_email = "art@robotrace.dev"
written_at = "2026-05-04T20:08:14Z"The file is written with mode 0600, in a directory chmod'd to
0700. Don't commit it. Add .robotrace/ to your global
.gitignore if you keep dotfiles in a repo:
echo ".robotrace/" >> ~/.gitignore_globalEach profile is one logged-in deployment. Most teams only need
default; if you talk to staging and production from the same
laptop, run:
robotrace login --profile staging --base-url https://staging.robotrace.dev
robotrace login --profile prod --base-url https://app.robotrace.devThe Python SDK reads default automatically. To target a different
profile in code, pass api_key= and base_url= explicitly — full
profile-aware loading is on the roadmap.
Resolution order
When you import robotrace, the SDK resolves credentials in this
order:
- Explicit kwargs —
Client(api_key=..., base_url=...) - Environment variables —
ROBOTRACE_API_KEY,ROBOTRACE_BASE_URL - The
defaultprofile from~/.robotrace/credentials - Otherwise:
ConfigurationError
This means CI machines that already inject env vars don't need to
run robotrace login — and if you change a key in the portal, the
env var still wins until you remove it.
whoami
Prints which deployment + email the saved profile is for, without exposing the key:
$ robotrace whoami
Profile: default
User: art@robotrace.dev
Client ID: 9f8e7d…-c1a2
Base URL: https://app.robotrace.dev
Saved at: 2026-05-04T20:08:14Z
File: /Users/art/.robotrace/credentialsAdd --json if you want to pipe it into a CI report.
logout
Removes the saved profile from your machine:
$ robotrace logout
✓ Removed profile 'default' from ~/.robotrace/credentials.
Note: the API key minted for that login is still valid until you
revoke it from the portal → API keys.logout does not revoke the underlying API key on the server —
that's intentional, so you can move credentials between machines
without invalidating the key. To kill the key for good, head to
Portal → API keys and click Revoke next to the row labelled
robotrace-cli (XKDF-PQ4N).
Why not just paste an API key?
Because the first thing every developer evaluating the SDK does
is type something into a terminal. If that something is "follow this
link, click a button, watch the URL print itself," the SDK feels
alive. If it's "go to the portal, click around, copy a 47-character
secret, paste it into .env.local, hope you didn't typo," the SDK
feels like a side project.
Both flows still work — the admin can mint a key the old-fashioned
way for CI environments — but robotrace login is the one we'd
rather you start with.
Troubleshooting
"Login window expired before the device was authorized" — the
flow has a 10-minute TTL. Run robotrace login again.
"Device login was refused" — somebody (probably you, in the other tab) clicked Refuse on the verification page. No state change on the server side; safe to re-run.
The browser didn't open — pass --no-browser and copy the URL
by hand. Or set BROWSER= in your environment if you have a
preferred browser opener.
No ~/.robotrace/credentials after a successful login — check
$ROBOTRACE_HOME. If it's set, the file lives at
$ROBOTRACE_HOME/credentials instead. We respect the override so
tests and CI runs can isolate from the user's real config.