# Meta Accounts Center API > **Box is the main surface.** All operator work goes through Box (box.muse-dev.online). The web UI, `box` CLI, and agents share the same API endpoints. No UI-only powers. **Status:** active (2026-10-03) **Motivation:** the phone number used in the muse.ai OTP pilot is linked to a Facebook account with its own credentials. Meta account management must be a separate, isolated API — never tangled with the phone-OTP flow. ## What it is A dedicated operator CLI + privileged gateway for automating [Accounts Center](https://accountscenter.meta.com/) operations: the Meta hub where Facebook, Instagram, WhatsApp, and Meta accounts are linked, with shared login, security settings, and connected experiences. It is **not** a general Facebook/Instagram automation tool. It manages the *account linkage and security surface* — which accounts are linked, how they log into each other, 2FA/password state, login activity. Content operations (posting, messaging) are out of scope. ## Separation guarantees 1. **Credential namespace:** uses `meta-creds.sh` types `facebook` / `instagram` only. The phone number is a *contact method* on the Facebook account, never a credential identifier. The `phone-otp` route (muse.ai SMS flow) and Meta credentials never share IDs, profiles, or audit logs. 2. **Browser/session reuse:** Meta automation runs in the agent's existing chrome-box profile — the same profile serves muse.ai and accountscenter.meta.com. The 1:1 rule (node == agent == profile) already gives us one egress per agent; no separate Meta node needed. The phone-OTP login that established the muse.ai session also establishes the Meta session, so read ops reuse it without re-authenticating. 3. **Authorization without exposure:** follows `CREDENTIAL-GATEWAY.md`. Agents know credential IDs and target actions; the privileged gateway decrypts from `store.age` and injects via CDP. Secrets never enter agent context, logs, or chat. 4. **Audit:** every call logs `{ts, credential_id, action, target}` — never values. Separate audit trail from phone-OTP operations. ## Operations ### Read (operator may run freely) | Command | What it returns | |---|---| | `meta-acct.sh list-linked ` | Accounts in the Accounts Center (type, username, linked since) | | `meta-acct.sh login-activity ` | Recent logins: device, IP/geo, time; flags unrecognized | | `meta-acct.sh security-status ` | 2FA on/off + method, login alerts on/off, password age | | `meta-acct.sh connected-experiences ` | Per-experience toggles: cross-app login, cross-posting, etc. | ### Write (human-gated via `/etc/netvm/approvals/`) | Command | Gate reason | |---|---| | `meta-acct.sh add-account --type ` | Links a new account; changes login blast radius | | `meta-acct.sh remove-account --account ` | Unlinks; some connected experiences don't revert cleanly | | `meta-acct.sh set-login-linking --on\|--off` | Controls cross-app login (see 2026 change below) | | `meta-acct.sh rotate-password ` | Credential change; invalidates sessions | | `meta-acct.sh set-2fa --method ` | Security-critical | Human gates use the existing `netvm-approve.sh` flow: the agent prepares the exact action, the human approves the specific `(credential_id, action, target)` tuple, the gateway executes once. ## The early-2026 login change Meta is removing the "Logging in with accounts" toggle: starting early 2026, **all accounts in the same Accounts Center log into each other by default**. Accounts Center membership *becomes* the login boundary. Implications for this API: - `list-linked` is now security-critical: anyone with one account's credentials can reach the others. - The API must surface a "blast radius" view: for a given credential, which accounts are reachable through the Accounts Center. - `remove-account` becomes the only way to revoke cross-app login — it must stay human-gated and be auditable. - Recommend: keep each client's accounts in their own Accounts Center; never mix clients (consistent with the one-client-one-credential-set rule). ## Architecture ``` Agent (untrusted) ──▶ meta-acct.sh (operator CLI) │ validates scope, checks human-gate ▼ Gateway (privileged, runs as operator) │ meta-creds.sh get │ decrypts store.age, never logs values ▼ CDP ──▶ accountscenter.meta.com (in the agent's chrome-box profile — same browser as the muse.ai session) ``` - Transport: Unix socket IPC between agent and gateway (not network). - Injection: CDP `Runtime.evaluate` into the Accounts Center DOM; falls back to field-focus + virtual keyboard only if DOM injection fails. - 2FA during a write op: the gateway pauses, requests the code through the existing OTP handoff (human relays), resumes. The code is transient — used once, never stored. ## Registry `ACCOUNTS.md` rows per Meta login, same discipline as today: `pending_auth` → `active`, `2fa-pending` where applicable. The `notes` column records the Accounts Center linkage (e.g. "facebook + instagram linked in one Accounts Center; blast radius = 2 accounts"). No credential values, no phone numbers. ## Change detection `bin/meta-ac-snapshot.py` turns the smoke test into a change detector. Each run captures a structural snapshot via CDP inside the node's netns and diffs against `snapshots/meta-ac/baseline.json`: - **redirect_chain** (normalized: query params stripped — nonces change every visit): seeded with the navigation target, then polls for where Meta sends us. Catches auth-flow reroutes. - **dom_markers**: form IDs, input names, button labels. Catches page restructures. - **final_title**: sanity signal. Outcomes: `PASS` (matches), `CHANGED` (structural diff — baseline untouched, human reviews then `--promote`), `FAIL` (automation broke). Snapshots are timestamped JSON; the diff doubles as the audit trail for what Meta changed and when. Validated 2026-10-03 on bl: headless Chromium in `warp-phone` netns via CDP reached accountscenter.meta.com → expected Meta auth redirect (auth.meta.com OIDC), login page renders. Baseline established; second run PASS. ## Out of scope (explicit) - Posting, messaging, ad management, Page/Portfolio operations. - The phone-OTP / muse.ai flow (separate API, separate credentials — see `docs/PHONE-OTP.md`). - WhatsApp linking (not available in all regions; revisit later). - Business Suite / Partner access (different trust model; separate spec if needed). ## Open questions 1. Which NetVM node hosts Meta automation — new dedicated node, or reuse an existing client node? (Lean: dedicated `meta` node, one egress.) 2. Should read ops also require the approvals flow for high-sensitivity accounts, or is operator-level enough? 3. Recovery codes: store in `store.age` (retrievable) or human-only? ## Implementation status (2026-10-03) **Read ops implemented** in bin/meta-acct.py + bin/meta-acct.sh: - list-linked AGENT — linked profiles from account_overview. Returns email (if present), profiles array with name/type (instagram, ai_device). Verified on pip (1 Instagram + Muse) and 646 (Muse only, no email). - security-status AGENT — security checkup action count + section list from password_and_security. - login-activity AGENT — "Where you're logged in" section text. (Currently returns the section header; the expandable session list needs a click-through — future work.) **Key finding:** the phone-OTP login for muse.ai leaves a full Meta session in the browser profile. accountscenter.meta.com loads without a login redirect. No separate Meta login needed — the agent profile IS the Accounts Center session. **Usage:** runs inside the agent's netns via meta-acct.sh wrapper: ~/Projects/NetVM/bin/meta-acct.sh list-linked pip Write ops (add-account, remove-account, etc.) remain human-gated per the spec above. Not yet implemented.