One connection, account-resolved tools
v2 chooses a persona through the URL. v3 connects to one URL and resolves its catalog from the account kinds you can reach. A person who can administer an organisation, sell from a Seller Account, and buy through multiple Buyer accounts can move between them without registering multiple MCP servers, by naming the account on each call. A call that names its account is scoped to that call and neither reads nor changes the connection’s stored selection, which stays only as a fallback for calls that omitaccountId until removal. Begin with get_status with no
accountId: it lists the accounts you can reach. Then name the account you work
in with accountId on every call. See
Name the account on every call.
Name the account on every call
Every v3 tool takesaccountId, the account the call runs in, and your tool
list marks it required (except on save_account and
save_directed_campaign_subscription, where it stays optional until the old
shape is removed). Send it on every call, reads included, with the
accountId from get_status. get_status is the one tool where it is
optional: with it, get_status reports that account; without it, it lists the
accounts you can reach. The call runs in that account
for that call only: nothing about the connection changes, so another
conversation on the same connection is never moved. This is the way to work in
more than one account at once.
Five inputs used accountId for something else before: member reads in get
(now memberAccountId), save_account (now id),
save_directed_campaign_subscription (now providerAccountId), the
map_account action of buyer_sellers_page (now providerAccountId) and
the connection account on the Media Partners Page (now connectionAccountId
on open_media_partners_page). On the first four,
accountId names the call’s account only when you also send the new input.
Without it, accountId keeps its earlier meaning for now, except on a
save_account call that creates an account (it carries accountType), where
accountId always names the account the call runs in.
open_media_partners_page has no such fallback: its accountId always names
the account the call runs in. It takes the connection account to open as
connectionAccountId, from the connection’s accounts[].id, and the seller to
open as sellerAccountId, the seller’s Account id. A call that still sends
sellerId is refused with an error that names sellerAccountId. V3 no longer
has open_connections_page, the tool’s earlier name: a call to it is refused as
an unknown tool. Use open_media_partners_page.
The server checks that you can work in the account you name: a membership in
it, or an administrator role on its parent organization. Naming an account never
grants access you do not already have. A Scope3 staff member who names an
account they are not a member of gets the platform support access that applies
to it and nothing more.
For now, a call that names no account still runs in the account the connection
last switched to, or in your credential’s home account, and its result ends with
a line asking you to send accountId. switch_account is retired: it is in no
tool list, and it still runs from tool lists that hosts cached earlier, with a
result that says so. expectedAccountId is no longer listed either, and the
server still accepts it. The removal date will be announced in advance. Send
accountId instead.
Credentials fixed to one account (WorkOS user and organization API keys, machine
credentials, buyer agents) can name only that account. The v3 HTTP API names
its account with accountId in the request too; its X-SCOPE3-CUSTOMER-ID
header is deprecated, and if both are sent they must name the same account.
Writes that omit the account
An account switch used to move every conversation on a connection, not just the one that asked. That is why a write has to say which account it is for. A write names it withaccountId, which never grants access and never changes any
selection.
While the transition runs, a write that omits accountId is still checked. If
your connection can be switched to another account, the write fails with
ACCOUNT_CONFIRMATION_REQUIRED. That covers people signed in to a connector and
personal (legacy) Scope3 API keys. WorkOS user and organization API keys and
machine credentials are fixed to one account, and so is the v3 HTTP API, so no
switch can move them and an omitted accountId runs in their account. The
server enforces this whatever tool list your client has cached.
expectedAccountId is no longer listed. The server still accepts it, and it
must agree with accountId when both are sent.
ACCOUNT_CONFIRMATION_REQUIRED names the company of the account the connection
is in, and gives no accountId, so take it from what the conversation
established or from get_status, and confirm it with the person before you
resend.
Orientation is always available
get_status returns:
- the account you named and its account kind, or, when you name none, the accounts you can reach;
- readiness and transaction state when available;
- blockers and typed next actions;
- ready buyer destinations where applicable; and
- a bounded list of reachable accounts that says when it is truncated.
get_status remains available in an unclassified account. This is a
navigation contract, not access to buyer or seller account data.
A Developer account (account kind developer) is an account that owns agents.
It has no buyer or seller nouns in v3, so it lists only get_status, and
get_status describes it with the unclassified
orientation state while reporting kind: developer. Developer accounts are in
a closed preview.
Nouns and verbs
v3 uses a small verb vocabulary over account-owned nouns:
Reads can share a safe envelope. Writes cannot: each
save_<noun> has its own
schema so a model does not choose an operation name and then guess the body.
Tool discovery is part of the contract
The catalog may differ by:- account kind;
- permissions;
- feature rollout; and
- MCP App support in the host.
tools/list as the source of truth for names and JSON Schemas for the
account kinds you can reach. If your principal can reach both a Buyer Account and a Seller
Account, it lists the deduplicated union of both catalogs at connection time.
Those tools are still authorized only in the account a call names: get_status
lists the accounts you can reach and the kinds represented in the listed
catalog.
The tool catalog explains the stable families
and intended workflows; it does not replace runtime discovery.
A call from a cached tool list that still runs switch_account makes v3 emit
notifications/tools/list_changed. Hosts can cache their initial tool list, so
they must not rely on that notification: a dual-access principal already sees
both account kinds, and a wrong-account call returns a typed error that names
the account the call ran in, the required account kind, and the accounts you can
reach that can run the tool. It tells you to name one of them with accountId.
Confirm with the person which account they mean.
Tasks, Pages, and model visibility
Some work should not be represented as model-authored fields. OAuth grants, credentials, payout destinations, legal acceptance, and certain review actions belong in a human-facing Page.- A model-facing tool may return a Page as the next action.
open_pageopens a named Page with bounded focus parameters.- Resource-bound MCP App tools may be present on the wire but hidden from model context by their visibility metadata.
- A Page stays in the account it opened in. Its calls name that account with
accountId, so they keep running there whatever another conversation does. In the Apostra web app, a Page whose browser tab has since moved to another account is refused withACCOUNT_MISMATCH, and a Page in an account you can no longer reach is refused withACCOUNT_NOT_REACHABLE. Nothing is read or written either way; reopen the Page in the account you want.
Declarative writes
Asave_<noun> call states the desired noun state. Responses distinguish:
createdorupdated— a write occurred;unchanged— the current state already matched;pending_approval— a governed or seller-applied change was staged, not committed; the object keeps its live state, and a media buy’spendingChangedescribes the waiting change;- a
*_requestedaction (cancellation_requested,pause_requested,resume_requested) — the seller accepted the change and is still applying it, so the object keeps its live state until the seller confirms; and - an error or partial-write result — the response explains what did and did not persist.
Concurrency and idempotency
These mechanisms answer different questions:expectedRevisionprevents overwriting a newer object state. Re-read after aREVISION_CONFLICTand decide whether the intended change still applies.idempotencyKeydeduplicates one logical attempt where the tool promises it. Reusing a key for different intent is an error; inventing a new key for every retry can duplicate work.correlationId, where offered, is a tracing handle and is not necessarily an idempotency key.
Includes keep diagnostics attached to the object
Health, sync history, and capability evidence are facts about a resource, so they are generally requested throughget.include:
Pagination and completeness
Pass the returned opaquenextCursor back to the same query while hasMore is
true. Do not edit, reuse across kinds, or interpret a cursor. A partial response
must say what was not reached; an empty items array alone is not evidence that
the underlying system contained no results.