Rivian Auth Reference
Rivian does not publish a supported public owner API. Riviamigo follows the same
cloud API shape used by the unofficial Home Assistant Rivian integration and the
rivian-python-client package.
Upstream References
- Home Assistant integration:
https://github.com/bretterer/home-assistant-rivian - Client library pinned by that integration:
rivian-python-client[ble]==2.0.0 - Source file to compare when auth breaks:
rivian/rivian.py - Rivian Roamer account-linking guide:
https://rivianroamer.com/guides/account-linking rivian-lsimplementation notes:https://github.com/pfrederiksen/rivian-ls/blob/main/CLAUDE.md
Current Endpoints
- GraphQL gateway:
https://rivian.com/api/gql/gateway/graphql - Charging GraphQL:
https://rivian.com/api/gql/chrg/user/graphql - Vehicle state WebSocket:
wss://api.rivian.com/gql-consumer-subscriptions/graphql
Required Headers
Base GraphQL requests should include:
User-Agent: RivianApp/707 CFNetwork/1237 Darwin/20.4.0Accept: application/jsonContent-Type: application/jsonApollographql-Client-Name: com.rivian.ios.consumer-apollo-iosdc-cid: m-ios-<uuid>
After creating a CSRF session, login and OTP requests also include:
Csrf-Token: <csrfToken>A-Sess: <appSessionToken>
Authenticated GraphQL requests generally use:
A-Sess: <appSessionToken>U-Sess: <userSessionToken>Authorization: Bearer <accessToken>
The GraphQL WebSocket uses the graphql-transport-ws subprotocol. Its
handshake carries A-Sess, U-Sess, and Csrf-Token; connection_init also
sends u-sess in the payload.
The worker starts two subscriptions on the same authenticated socket: the
partial vehicleState($vehicleID: String!) stream and
chargingSession($vehicleID: String!). The latter is the live charging source;
its liveData contains power, energy, range, elapsed/remaining seconds, price,
currency, and charger state, while chartData contains observed SoC/power
points. getLiveSessionHistory($vehicleId: ID!) remains a REST reconciliation
query after a session, not a replacement for the live stream.
Login Flow
- Call
CreateCSRFToken. - Save
csrfTokenandappSessionToken. - Call
Loginwith email/password and the CSRF/app-session headers. - If Rivian returns
MobileMFALoginResponse, saveotpTokenplus the CSRF session and ask the user for the MFA code. - Call
LoginWithOTPwith email,otpCode, andotpToken. - Save
accessToken,refreshToken,userSessionToken,appSessionToken,csrfToken, and the creation timestamp. - Call
getUserInfoagainst the same gateway to readcurrentUser.vehicles[].id,vin,name,vehicle.modelYear, andvehicle.model. /v1/vehicles/connectreturns those vehicle summaries. The browser must follow a successful connect by callingPOST /v1/vehicles, which encrypts and persists the temporary token bundle from Redis intoriviamigo.vehicle_credentialsand setsusers.default_vehicle_idif it is currently empty.
Refreshing credentials for an existing vehicle uses the same encrypted storage
path and immediately sends a worker-start command. This matters after a restore:
provider credentials are intentionally redacted from recovery packages, so the
vehicle can exist in PostgreSQL without a credential row. Startup still runs the
vehicle worker long enough to replace stale authorized/connected state with
an actionable needs_reauth status. After the user reconnects the Rivian
account, the supervisor replaces that completed worker and starts ingestion
immediately. A successful refresh should therefore be followed by worker health
and telemetry-timestamp checks, not only by an authorized database state.
Advisory Renewal Policy
Riviamigo treats the durable token bundle creation time as the most recent full
Rivian authentication. The API derives expected_renewal_at at 180 days and a
typed renewal_state: healthy, renewal_soon, renewal_due, or
reauth_required. The seven-day renewal_soon threshold is an application
policy informed by current unofficial-client behavior, not an official Rivian
expiry guarantee. Observed credential rejection always overrides the timer.
A successful full login or OTP credential refresh resets the creation time. Automatic CSRF/app-session renewal preserves it because that rotation does not represent a new long-lived Rivian authorization. Do not move the advisory date from routine WebSocket reconnects or short-lived session refreshes.
The credential-refresh route verifies the selected Rivian vehicle before
storage, starts the worker, and waits for runtime health plus vehicle discovery
for a bounded interval. It returns telemetry_status: connected only after that
proof. A bounded timeout returns waiting_for_vehicle_data with a recoverable message while
retaining the valid encrypted credentials. The browser invalidates the vehicle,
status, and health queries and explicitly reconnects the live vehicle socket
after either result.
Local Riviamigo Auth Gotcha
/v1/vehicles/connect is protected by Riviamigo's own JWT. A browser-side
401 Unauthorized on that route can mean the local 15-minute Riviamigo access
token expired before Rivian was contacted. The web API client retries protected
requests once after calling /v1/auth/refresh.
If the UI returns to "Add a Vehicle" after a successful Rivian login, check:
- Redis key
rivian:connect:<user_id>exists: Rivian auth succeeded but has not been persisted. riviamigo.vehicleshas no row for the user: the browser did not completePOST /v1/vehicles.users.default_vehicle_idis null: the dashboard will continue to show the empty vehicle state even though Rivian auth temporarily succeeded.