# Auth.md

## Authenticating an agent with Decoy

Use `https://api.decoys.me` for HTTP requests or connect through the Decoy MCP bridge. Follow [the access guide](https://www.decoys.me/docs/access.md) to choose account and memory permissions; inspect `tools/list` for MCP inputs.

Decoy's public documentation, documentation-search tools and download information require no login. Access to a user's Decoy accounts and credentials requires that user's approval.

Follow the [whole-journey conversation contract](https://www.decoys.me/agent-experience.md): handle routine authentication and recovery in the runtime, and involve the person only for a meaningful decision or necessary action. Reuse known registration and connection state; do not repeat onboarding questions.

## User registration and sign-in

1. The user downloads [Decoy from the App Store](https://apps.apple.com/app/decoy-masked-email-password/id6759507252) on their iPhone.
2. The user opens Decoy and uses Sign in with Apple to register or sign in, then finishes vault setup in the app.
3. The agent starts a connection using the local MCP bridge or the device authorization flow below.
4. The user opens the returned Decoy connection page and reviews the requested access in the iPhone app. They can approve it as asked, remove parts, or add access; what they approve is the grant. On a computer, the page shows the QR code to scan in Decoy; on the iPhone, it opens Decoy.
5. Only after approval can the agent collect its access token and use the granted permissions: everything granted, including anything the user added.

If the user has not installed and registered in Decoy, send them the App Store link first. Have them finish setup, then start a fresh connection if the previous request expired. The agent cannot register an Apple account, create the user's vault or approve its own access. Never ask the user to share an Apple password or a Decoy session token with the agent.

The iPhone remains the approval device. Browser extensions are available for Chrome and Firefox.

## Optional: local MCP bridge

Run `npx decoy-mcp-server` in the user's intended environment. Open the Decoy-owned connection URL returned by the bridge. The user approves the requested accounts, Personal Alias, memories, levels and duration in the iPhone app. Connect the MCP client to the local endpoint advertised by the bridge, normally `http://localhost:3001/mcp`.

The bridge keeps its private key locally and decrypts approved information in the integrating runtime. Do not expose that localhost endpoint publicly, send its saved token to a language model, or replace the Decoy-owned approval screen with a custom QR prompt.

The public documentation MCP endpoint at `https://www.decoys.me/api/docs-mcp` only searches public docs; it cannot connect to a vault or retrieve credentials.

## Direct HTTP integration

- API issuer: `https://api.decoys.me`
- Authorization metadata: <https://api.decoys.me/.well-known/oauth-authorization-server>
- Protected-resource metadata: <https://api.decoys.me/.well-known/oauth-protected-resource>
- API specification: <https://www.decoys.me/openapi.json>
- Detailed setup: <https://www.decoys.me/documentation.md>

Discover endpoints and supported scopes from these documents. Use the advertised OAuth device grant. Open the returned `verification_uri_complete` for the user; keep approval on the Decoy-owned page. Poll at the server-provided interval, handle `authorization_pending` and `slow_down`, and stop on denial or expiry. After approval, send the access token as an Authorization Bearer header to the API. Do not place tokens in URLs.

### Decoy device authorization

The authorization-server document's `agent_auth.skill` links to this file. Its `decoy_device_authorization` object is a **Decoy-specific discovery extension** describing the existing RFC 8628 device grant. Standard OAuth clients should use the top-level `device_authorization_endpoint`, `token_endpoint` and `grant_types_supported` fields. `identity_types_supported` is empty: Decoy does not implement the separate WorkOS `service_auth`, `anonymous` or `identity_assertion` registration profiles, email claims, ID-JAG exchange, or pre-approval access.

Use the [HTTP walkthrough](https://www.decoys.me/agent-recipes.md) for the complete request bodies, helper and first-email test. Start with bounded authorization_details at the discovered PAR endpoint, then send its request_uri and your public key to the device endpoint. No duplicate scope parameter is required. For credentials and API keys, use the [protected-storage recipes](https://www.decoys.me/agent-storage.md). For memories use the [memory tools and per-memory API](https://www.decoys.me/agent-memories.md), not an account-notes convention. The email helper is not either write implementation.

Keep device_code secret: it is not user_code. Share verification_uri_complete unchanged; poll using the secret device_code at the returned interval. Inspect bounded/reach and final effective grants. authorization_pending means no token yet, not proof the user has not tried to approve.

The walkthrough provides a dependency-free Node helper. Follow the returned approval link without asking the user to identify their app version. If Decoy reports an incompatibility, use its documented recovery. Crypto runs in the integrating runtime, not by sending private keys or protected plaintext to Decoy cloud.

## Reuse and reconnect

To check a stored connection, call GET `/api/agent/policies` on its original API host with its bearer token. A successful response starts with `access`: what the connection can do now, in plain terms. Read it at the start of every session and whenever a call is refused for access (`403 INSUFFICIENT_GRANT`, `GRANT_EXPIRED` or `SCOPE_MISSING`), and reuse the connection if it is sufficient ([how to read it](https://www.decoys.me/docs/access.md#what-you-can-do-now)). For a 401, follow its error code and recovery. There is no unauthenticated connection lookup.

Decide what you can do from `access`, never from `scopes` (a ceiling of capabilities that does not list memory access) or from what you saved at pairing: the owner changes access in the app. Token collection and refresh return `access_version`; keep it with your notes and read `access` again when it differs. For a row's selectors and expiry, check all `policies[].permissions` and the top-level `implicit` array. The [HTTP walkthrough](https://www.decoys.me/agent-recipes.md) includes an illustrative partial-approval response and recovery cases. Valid credentials keep their existing keypair. Refresh rotates the access and refresh tokens on the same connection; it does not restore a missing private key or renew an expired grant. A missing key is replaced through an owner-approved reconnect, described next. An agent whose sandbox is wiped after every task, and whose own browser keeps its storage, can instead continue the same connection with no owner step, using the page at `https://agent.decoys.me` and the `keep` and `continue_ticket` fields of the device request ([continue a connection from your own browser](https://www.decoys.me/agent-recipes.md#continue-a-connection-from-your-own-browser)). [Keep your connection across sessions](https://www.decoys.me/agent-recipes.md#keep-your-connection-across-sessions) lists what to store, where (a file, a secret store or environment variable, or nothing), and what losing each value costs.

An owner-approved reconnect to an active connection carries active grants and account/address creation provenance to the replacement token. To be offered one, start a new approval with the same `client_id` the connection was created with; the owner's app then shows Reconnect for that connection, and the owner decides. A brand-new pairing does not inherit them merely because the client name matches. A revoked connection cannot be reconnected. Request the same access on a reconnect as at the first pairing: it keeps the connection's current access, and a narrower request removes nothing. Read `access` after reconnect before asking for additional permissions, and ask for them on the connection, not with a new pairing link. Preserve the existing keypair when available. With a changed key, access and created accounts continue at once. Earlier memories, mail, account fields the agent wrote and the account name have to be shared to the new key by the owner's app: until then a read answers `preparing` with `waiting_on: "owner_phone"` and `retry_after_seconds` while Decoy asks the owner's phone in the background, for up to 7 days, and then `failed` with `recovery: "stopped_asking"`. A name reads `metadata_seal_state: "preparing"`, then `"waiting"`, and never `failed`. The reconnect takes effect when the agent collects the new token, not at approval: collect promptly, and a pairing that is never collected leaves the earlier connection as it was ([field names and what to do](https://www.decoys.me/agent-recipes.md#keep-your-connection-across-sessions)).

The MCP bridge keeps its key and reconnects with it: `npx decoy-mcp-server --reconnect`, for a connection first paired with 1.2.1 or later. It never makes a new key for an existing connection, so a lost key file means a new connection. A hosted runtime supplies the connection as `DECOY_AGENT_KEY` and `DECOY_AGENT_TOKEN`. See [the MCP bridge: files, environment and recovery](https://www.decoys.me/agent-recipes.md#the-mcp-bridge-connection).

## Errors and revocation

Where present, follow the response's `code` and `recovery`. Missing or invalid credentials require repairing client configuration. For expired or replaced credentials, follow the returned recovery; do not assume a new pairing is required. Revocation requires new approval and cannot be undone by reconnect. Missing scopes and expired resource grants require the specific approval indicated by the server. Older deployments may return only `Unauthorized`; do not infer revocation from that message alone.

Users can revoke access through Decoy's Connected Agents controls. Clients that own the token can use the advertised OAuth revocation endpoint when the user requests it. Revocation stops future access; it cannot erase secrets already received by an agent.

Permission to retrieve a credential is not permission to purchase, send messages or change settings on another website.
