# Smartcar OAuth — Frontend integration cheat sheet

## Tank Track API base (staging example)

```
https://api.tanktrack.thesuitchstaging2.com:6260/api/v1
```

Use that segment as **`{BASE}{API_PREFIX}`** in every endpoint below (`/smartcar/login`, `/smartcar/complete-connect`, etc.). Production may use another host/port; **`data.oauthRedirectUri`** and **`completeConnectPOST`** returned from **`GET …/smartcar/login`** reflect what the API is configured with.

Other environments can be described as **`{BASE}{API_PREFIX}`** where `API_PREFIX` defaults to `/api/v1` unless the server sets `API_PREFIX`.

### Backend behavior (what you see in the browser)

When **`SMARTCAR_SIMPLE_OAUTH_REDIRECT=true`** (default on many deploys), after Smartcar returns to the API, the server sends a **302** redirect to **your SPA** (not to a JSON endpoint). That includes **JWT recovery**: if the API cannot link via `state`/session, it still **302** to **`TANK_TRACK_APP_HOME_URL`**, **`SMARTCAR_APP_HANDOFF_URL`**, or **`SMARTCAR_OAUTH_FAILURE_URL`** (first available in the backend’s failure / handoff resolution order) with **`oauth_code`** and **`recover=jwt`** (plus **`smartcar=error`**, **`reason=…`**), so the browser lands on the SPA with recovery query params instead of only **`GET …/smartcar/oauth-done`**. Your SPA then runs **§3** → **§2** when those params appear. If no HTTPS handoff URL is configured on the API, the user may remain on **`/smartcar/oauth-done`** (HTML) until env is fixed.

### Smartcar vs Tank Track ([Handle the Response](https://smartcar.com/docs/connect/handle-the-response))

Smartcar returns **`code`**, **`user_id`**, and **`state`** on success. Tank links your user when **`state`** (from **`GET …/smartcar/login` → `link`**) survives the redirect or when a session cookie bridges the flow. **`user_id`** matches an existing **`SmartcarAccount`** only **after** a prior successful link. Otherwise the SPA must **`POST …/complete-connect`** with **`{ "code": "…" }`** and your logged-in JWT (recovery).

---

## 1) Start Connect — **`GET …/smartcar/login`**

**Staging example**

```
GET https://api.tanktrack.thesuitchstaging2.com:6260/api/v1/smartcar/login
Authorization: Bearer <user JWT>
```

**Frontend implements:** authenticated call → read `data.link` → open that URL in the **same user agent** where possible (in-app browser / WebView / system browser consistent with product choice).

### Request

| Part | Value |
|------|--------|
| Method | `GET` |
| Headers | `Authorization: Bearer <user JWT>` (same JWT as rest of Tank Track APIs) |

No body.

### Response — **200** (success)

```json
{
  "message": "Smartcar OAuth",
  "data": {
    "link": "https://connect.smartcar.com/oauth/authorize?...",
    "userId": "507f1f77bcf86cd799439011",
    "email": "user@example.com",
    "oauthRedirectUri": "https://api.tanktrack.thesuitchstaging2.com:6260/api/v1/smartcar/redirect",
    "redirectUrlExampleOnSuccess": "https://your-spa/dashboard?smartcar=ok&...",
    "completeConnectPOST": "/api/v1/smartcar/complete-connect",
    "vehicleWebhookCallbackPOST": "https://api.tanktrack.thesuitchstaging2.com:6260/api/v1/smartcar/webhook",
    "simpleBrowserRedirectEnabled": true,
    "workflowNote": "(string hints for debugging)"
  }
}
```

Use **`data.link`** only (do **not** build the Smartcar authorize URL yourself; do **not** drop `state=` out of Smartcar’s URL).

### Response — **401**

```json
{
  "error": "missing_user",
  "message": "Smartcar link requires a Tank user id from your JWT (logged-in session)."
}
```

---

## 2) Finish Connect (recovery) — **`POST …/smartcar/complete-connect`**

**Staging example**

```
POST https://api.tanktrack.thesuitchstaging2.com:6260/api/v1/smartcar/complete-connect
Authorization: Bearer <same JWT as /smartcar/login>
Content-Type: application/json
```

**Frontend implements:** when the user lands on **your SPA** after OAuth with **`recover=jwt`** and **`oauth_code`** (often via **302** from the API when `SMARTCAR_SIMPLE_OAUTH_REDIRECT=true` — see header above), call this endpoint with the stored user JWT and the one-time `code`.

### Request

| Part | Value |
|------|--------|
| Method | `POST` |
| Headers | `Authorization: Bearer <same JWT as /smartcar/login>`, `Content-Type: application/json` |
| Body | JSON (see below) |

**Body (required)**

```json
{
  "code": "<paste from Smartcar redirect ?code= … or from ?oauth_code= on your SPA handoff URL>"
}
```

### Response — **200** (linked)

```json
{
  "linked": true,
  "smartcarUserId": "smartcar-user-uuid",
  "redirectUrl": "https://your-spa/... ?smartcar=ok&smartcar_uid=smartcar-user-uuid&native_deeplink=tanktrack%3A%2F%2F..."
}
```

Frontend may navigate to **`redirectUrl`** or show success and refresh “vehicles” / profile.

### Response — **401**

```json
{
  "message": "UnAuthorized Request",
  "error": "user_missing"
}
```

### Response — **400** (bad/expired Smartcar exchange)

```json
{
  "error": "invalid_grant | invalid_client | code_required",
  "message": "Human-readable explanation"
}
```

- `invalid_grant`: code already used / expired → user must tap **Connect Smartcar** again.
- `invalid_client`: backend misconfigured (not fixed in frontend).
- `code_required`: missing or non-string **`code`** in JSON body.

### Response — **502**

```json
{
  "error": "smartcar_error",
  "message": "…"
}
```

---

## 3) Where the frontend reads the one-time **`code`** (no JSON — URL only)

Smartcar redirects the browser to the **backend** OAuth URL first (`GET …/smartcar/redirect` — not normally implemented in frontend).

When **`SMARTCAR_SIMPLE_OAUTH_REDIRECT=true`**, the API then **302** the browser to **your SPA** (HTTPS handoff / home / failure URL from server env), or to **`GET …/smartcar/oauth-done`** only if no HTTPS handoff URL is configured. JWT recovery uses the same **302** + query params on the **SPA** URL when handoff is set (see top of this doc).

### Success landing (typical SPA query params)

After a successful backend exchange:

- **`smartcar=ok`** (value `ok`)
- **`smartcar_uid=<Smartcar user UUID>`** (Smartcar-side id — not Tank Mongo `_id`)
- Optionally **`native_deeplink=<url-encoded deeplink>`** so the SPA can open the native app.

Example:

```text
https://your-spa/dashboard?smartcar=ok&smartcar_uid=a1b2c3d4-e5f6-7890-abcd-ef1234567890&native_deeplink=tanktrack%3A%2F%2Fhome
```

**Frontend:** treat `smartcar=ok` as “linking succeeded”; optionally read `smartcar_uid` for UX; optionally open `native_deeplink`.

### JWT recovery landing (SPA must call **§2**)

When linking could not tie the browser session to Tank on the backend, users are redirected to HTTPS handoff (**when configured**) with:

- **`smartcar=error`**
- **`reason=…`** (e.g. `use_post_complete_connect_with_jwt_and_code`)
- **`oauth_code=<Smartcar authorization code>`**
- **`recover=jwt`** (preferred; backend also tolerates malformed `recover-jwt` keys on some hops)

Example:

```text
https://your-spa/smartcar-return?smartcar=error&reason=use_post_complete_connect_with_jwt_and_code&oauth_code=b2493e73-3ff6-43bf-9fbf-4eaaf26a3801&recover=jwt
```

**Frontend:**

1. If `recover=jwt` and `oauth_code` are present → **`POST https://api.tanktrack.thesuitchstaging2.com:6260/api/v1/smartcar/complete-connect`** with body `{ "code": "<oauth_code>" }` and the logged-in JWT.
2. On **200**, clear flags from URL and navigate to success (or hit `redirectUrl`).
3. On **400** (`invalid_grant`), send the user through **§1** again.

### Optional introspection (**not** usually needed for SPA)

```http
GET https://api.tanktrack.thesuitchstaging2.com:6260/api/v1/smartcar/oauth-done?ok=0&recover=jwt&oauth_code=<code>&format=json
Accept: application/json
```

**Response — 400** (recovery hints), includes when server has SPA handoff env:

```json
{
  "ok": false,
  "reason": "use_post_complete_connect_with_jwt_and_code",
  "recover": "jwt",
  "oauthCode": "…",
  "recovery": "POST https://api.tanktrack.thesuitchstaging2.com:6260/api/v1/smartcar/complete-connect with Authorization: Bearer …",
  "smartcarConnectDocs": "https://smartcar.com/docs/connect/handle-the-response",
  "recoveryHandoff": "https://your-spa/…",
  "recoveryAppDeeplink": "tanktrack://… ?oauth_code=…&recover=jwt"
}
```

`recoveryAppDeeplink` is present only if **`TANK_TRACK_APP_DEEPLINK`** is set on the API.

---

## Frontend checklist (minimal)

| Step | Action |
|------|--------|
| 1 | **`GET https://api.tanktrack.thesuitchstaging2.com:6260/api/v1/smartcar/login`** (+ JWT) → `data.link` |
| 2 | Open **`data.link`** in browser / WebView |
| 3a | SPA sees **`smartcar=ok`** → done (refresh data / optional deeplink) |
| 3b | SPA sees **`recover=jwt` + `oauth_code`** → **`POST https://api.tanktrack.thesuitchstaging2.com:6260/api/v1/smartcar/complete-connect`** with `{ "code": "<oauth_code>" }` + JWT |

---

Backend reference: `src/controllers/smartcarController.ts`, routes `src/routes/smartCarRoutes.ts`.
