---
title: Account API expansion
description: Which account capabilities the Shop API serves and which the storefront runs itself.
---

Account capabilities split by who performs them. The Shop API serves the shared ones: addresses (list, add, delete), the
card list, the profile read, orders, identity and the three checkout reads. The storefront calls them through the typed
`@retail-demo/api/client` (`callAsAccount(actor, (api, options) => api.me.profile.get({}, options))`), with a minified, route-only contract. The client provides TypeScript types and routing; the API validates
inputs at runtime. Full-contract clients, such as MCP and the backoffice, also validate before sending. Failures arrive as `ORPCError`s whose problem has an
`error` member, and the storefront maps on that member, not on the status. Everything only the storefront performs runs
in the storefront Worker through `shopOperation` against the shop database and the shared `AccountWriter`
(see `docs/architecture.md` in the repository): the profile page,
every card control, address update and default, activity, connected agents, invitations, delegated sign-in and the
account-security family. Human-only operations require `account:security` and refuse invited agents, Poppy, delegated
OAuth clients and operators; they are not API routes and are excluded from agent discovery and grants.

## Embedded verification and credentials

The storefront runs embedded verification itself: the shared account operations own operation creation, encrypted
provider state, attempt leases and proof transitions, and the storefront executes the OAuth/MFA SDK call with its own
cookie. It verifies the returned ID token and checks subject, MFA, freshness and the original authority before proving
the offer. Concurrent or stale completions cannot settle another attempt. Provider jobs never enter browser action
results. The storefront retains refresh tokens only after rechecking the original account and its own session cookie.

## Invited agents and My Account

An invited agent's Auth0 callback is recorded by the storefront itself (`delegation.record`): it checks the ID token it
just verified against the new cookie (subject, session, website audience, a sign-in at most ten minutes old) and
records the session through `@retail-demo/identity/delegation`, which checks the live approval, both lifecycles and bounded
expiry. Sign-out ends that session the same way (`delegation.end`). The storefront reads the stable claim namespace even
when the login issuer changes. Later invited-agent requests for shared capabilities reach the API with the web agent forwarder and
a signed `invited-agent+jwt` context; identity reads compare raw verified token claims with the retained cookie
snapshot. Live identity returns only the account display name for an agent banner; it does not expose the customer's
email.

Auth0 My Account remains entirely website-owned. The API returns a boolean describing canonical database identity
eligibility. The storefront applies origin/RP topology rules, acquires My Account tokens from
its SDK, holds step-up credentials and executes provider requests. No My Account token, URL or transport moves to
the API.

The procedure tests in `apps/api/src/procedures` cover the shared endpoints, and the storefront capability tests
(`apps/webshop/tests/support/shop-capability.ts`) cover the rest.

## Routes

Routes are designed as resources: plural nouns, state changes as sub-resources, `201` with `Location` for creates and
`204` for deletes. Nothing aliases the old paths. The contract is the single place a route is named, so a rename changes
the contract and the e2e URLs only.

| Procedure               | Route                         |
| ----------------------- | ----------------------------- |
| `getIdentity`           | `GET /v1/me/identity`         |
| `getCheckoutAddress`    | `GET /v1/me/checkout-address` |
| `getCheckoutMethods`    | `GET /v1/me/checkout-methods` |
| `createCheckoutPreview` | `POST /v1/checkout-previews`  |

The three checkout reads sit with the shared capabilities rather than under a website prefix, and they are in the Poppy
document. Agents must call these paths; the earlier website-prefixed URLs no longer exist.

Private activity, connected agents, agent invitations and guest invitation acceptance and inspection are not API routes
any more: the storefront runs them itself through the account domain (`activity.*`, `agents.*` and `invitations.*`
operations). Disconnecting an agent revokes a personal connection in the shop database, or the Poppy grant at the
Auth0 proxy with the service credential, and records it in the private activity. The storefront also sends the invitation
email with its own email binding. Nothing retries a failed send: the owner can resend the invitation with a fresh link
once its send claim expires.

The fresh-intent id a request is approved by is the `freshIntentId` input field (not `operationId`, which names the
OpenAPI operation).

## Endpoints that replace the web-session surface

The API's web-session operations are gone, and so is every customer endpoint that took over a part of them: the Shop API holds no web-session surface at all. The guest-cart merge, the status receipt pages, the profile page, the email-verification sync after sign-in, the claim of verified guest orders and the whole account-security family (fresh verifications and the acceptance of their proof, security methods and topology, embedded verification, phone and email contact changes, contact methods and account deletion) are not endpoints: the storefront runs them itself through the shared domain operations. The sync is the one operation that needs no active account, because it finishes the email change that holds the account in a contact transition. Email change and deletion synchronise the Auth0 proxy lifecycle through the storefront's Auth0 proxy connection, and wake or end the open agent conversations through the shared `ConversationHub`.

## Provider failures and explicit retry

Email changes and account deletions attempt provider work during the request. A partial failure preserves the
approved operation, progress checkpoints and the account's denial state. The account may remain restricted until
that same operation is explicitly resumed; hourly jobs do not retry this provider work.

The customer can retry through the signed-out status page while their receipt remains valid. An operator can use the
dedicated recovery endpoint without needing the deleted user to sign in. Neither path creates a new authorization
or accepts an arbitrary provider command. See [account recovery](/runbooks/account-recovery) for credentials, the exact
request and response handling. Payment recovery continues through the [scheduled jobs](/runbooks/scheduled-jobs).

## Sign out everywhere

Sign out everywhere runs in the storefront, through the Management API, not in the Shop API. Disconnecting a connected agent
runs in the storefront too.

```mermaid
sequenceDiagram
  autonumber
  participant B as Browser
  participant W as Storefront
  participant KV as WEB_SESSION_REVOCATIONS
  participant M as Auth0 Management API
  B->>W: Sign out everywhere (identity check, fresh intent disconnect-all)
  W->>W: spend the fresh intent once through the revocation list
  W->>KV: write the account cutoff for sub
  W->>M: GET /api/v2/users/{sub}/sessions (read:sessions)
  M-->>W: sessions
  W->>W: keep sessions whose clients include the website client
  loop each kept session
    W->>W: deny linked Poppy grants through Auth0 proxy
    W->>M: POST /api/v2/sessions/{id}/revoke {preserve_refresh_tokens: false}
    M-->>W: accepted, provider cleanup may continue (404 counts as done)
  end
  W-->>B: this device is signed out
```

The cutoff is written first, so this device is signed out even if the Management API fails; the page then says "We
couldn't sign out your other devices". Auth0 sessions used only by another application stay outside sign out everywhere; Poppy grants linked to the revoked storefront browser sign-ins are denied first. Independent grants remain connected.
The storefront requests a Management token with exactly `read:sessions`, `delete:sessions` and `delete:refresh_tokens`.
Refresh tokens are revoked, but customer API tokens already issued stay valid until they expire.

The human-only sessions page combines Auth0 browser sign-ins and active signed-in Poppy sessions. Bounded provider
reads expose continuation, including empty historical batches, and partial failures remain visible. Approval links
identify the approving browser, never the agent's device. Missing approval browsers do not hide active agents.
Fresh verification binds each selected browser/session/grant action to its exact source ID. Ending a Poppy session
leaves its Account Token usable; disconnecting its exact grant signs out every session using that grant. Ending a
browser sign-in records the local cutoff and denies linked Poppy grants before requesting Auth0 revocation with
refresh tokens. Poppy-only actions leave the approving browser alone. Current-browser cleanup failures preserve
their warning and owned operation through logout and sign-in; expired verification can restart for the same target.
