# Tri-County Tire LLC — QBO Onboarding Runbook

**From:** Riv (Integrations)
**To:** Jimmie
**Date:** 2026-05-24
**Triggered by:** Ledger handoff (`2026-05-24 - Ledger to Riv - Tri-County Tire QBO Onboarding Request.md`)
**Time needed:** ~5 minutes
**Assigned slug:** `tct`

This wires Tri-County Tire LLC into the j2-qbo MCP server so Ledger can read reports/transactions and queue writes for your approval. All infra (Intuit Dev app, shared OAuth, multi-client config, MCP server, write queue) is already built — this is the per-client OAuth handshake only.

**Heads-up — flow changed since the Afton runbook (2026-05-18):** Production keys reject `http://localhost` redirects, so we no longer use a local callback server. The current flow is a **two-step paste-URL** pattern using Intuit's hosted OAuth Playground as the redirect. The script walks you through it.

---

## Step 1 — Decide tier before we start (~10 sec)

Two questions Ledger needs answered:

1. **Is Tri-County in the chairman group?** (Currently: VIP Play, Loop TV, FuzeBox AI — hard-fenced from the MCP server.)
2. **If not chairman, what's the default tier ceiling for first writes?** Leave null = no ceiling, all writes go through the queue normally. Set a ceiling if you want to cap automated activity until you've gotten comfortable with the client.

Default assumption: **non-chairman, no tier ceiling.** Tell me if either should change before I run the script.

---

## Step 2 — Kick off the authorize URL (~30 sec)

```powershell
cd C:\PKA\Atlas\app
python qbo_oauth_add_client.py --slug tct --name "Tri-County Tire LLC"
```

What happens:

1. Script prints an Intuit authorize URL and opens your browser
2. **Sign in with the Intuit/QBO account that has Company Admin access to Tri-County Tire's books**
3. If prompted with a company picker, select **Tri-County Tire LLC**
4. Click **Connect** to approve the access request
5. Browser lands on Intuit's OAuth Playground page (`https://developer.intuit.com/v2/OAuth2Playground/RedirectUrl?...`)

**Don't close the browser yet.** You need that URL.

If chairman-group: add `--chairman` to the command above.

---

## Step 3 — Paste the redirect URL back (~30 sec)

Copy the **full URL** from your browser's address bar (it'll have `?code=...&realmId=...&state=...`), then run:

```powershell
python qbo_oauth_add_client.py --slug tct --name "Tri-County Tire LLC" --paste-url "<paste URL here>"
```

Wrap the URL in double quotes — it contains `&` characters that PowerShell will otherwise interpret.

Expected output:
```
Exchanging auth code for tokens (realmId=...)...
Saved clients.tct to qbo_clients.json
  display_name = Tri-County Tire LLC
  realm_id = <some 16-digit number>
  chairman_group = False
```

---

## Step 4 — Sanity check (~10 sec)

The script prints this at the end. Paste it into the same PowerShell:

```powershell
python -c "from qbo_client import refresh_access_token, query; t = refresh_access_token('tct'); print(query('tct', t, 'SELECT COUNT(*) FROM Account'))"
```

Expected: a JSON blob with an account count (e.g. `{'totalCount': 73}`).

If you get a sensible number, OAuth is good and the realm is reachable.

---

## Step 5 — No MCP server restart needed

The j2-qbo MCP server reads `qbo_clients.json` fresh on every call (no caching). As soon as the file is saved in Step 3, the new `tct` slug is live.

**However:** any Claude Code session that was open *before* Step 3 may not see the new tools until the next call to `list_clients` re-runs. Easiest path is just to call `list_clients` again in Larry — `tct` will appear.

---

## Step 6 — Ping Larry

Reply to Larry that Tri-County Tire is wired up. I'll confirm with `list_clients`, then hand back to Ledger for first-pass diagnostics: COA review, last reconciliation date, AR/AP aging, anything else that needs cleanup before close work begins.

---

## If anything breaks

| Symptom | Fix |
|---------|-----|
| **"Slug 'tct' already exists"** | Someone already ran Step 2/3. Either confirm with me, or re-run Step 3 with `--force` to overwrite. |
| **"Redirect URI mismatch"** on Intuit's page | The OAuth Playground URL got removed from the app's redirect list. Re-add `https://developer.intuit.com/v2/OAuth2Playground/RedirectUrl` under your QBO app → Keys & OAuth (Production tab). |
| **"No 'code' found in --paste-url"** | The URL got truncated when you pasted (probably missing the `?code=...` portion). Re-copy the full URL from the address bar. |
| **"No 'realmId' found in --paste-url"** | Same as above — likely truncated paste, or you authorized against an account with no company selected. |
| **PowerShell complains about `&` in the URL** | Make sure the whole URL is wrapped in double quotes: `--paste-url "https://..."` |
| **Token exchange fails with 400/401** | The auth code is single-use and expires in ~10 minutes. Re-run Step 2 to get a fresh code, then redo Step 3 quickly. |
| **You signed in with the wrong Intuit account** | Sign out at qbo.intuit.com, re-run Step 2 from a fresh browser session. |
| **Sanity check (Step 4) returns an auth error** | Refresh token didn't save correctly. Ping me with the script output and I'll diagnose. |

---

## Reference

- Onboarding script: `C:\PKA\Atlas\app\qbo_oauth_add_client.py`
- Client config: `C:\PKA\Atlas\app\qbo_clients.json`
- MCP server: `C:\PKA\Team\Riv\j2_qbo_mcp\server.py`
- Prior runbook (now superseded by the paste-URL flow): `2026-05-18 - Riv to Jimmie - Afton QBO Onboarding Runbook.md`
