# Configure Configure is personalization infrastructure for agents. Configure personalizes your agent with user-approved context from other agents, app contexts, preferences, memories, and more. Architecturally, Configure is a user-controlled system of record across agents: it recognizes the same user across agents and lets approved agents read and write shared user context over API, MCP, or tool calls. ## ACCESS POLICY Configure is live for select teams. The SDK, docs, and package install are public. Production credentials are issued after Launch Preview approval. If CONFIGURE_API_KEY, CONFIGURE_PUBLISHABLE_KEY, and CONFIGURE_AGENT are present, use them. If credentials are missing, direct the user to get started and request access. Do not invent keys. ## KISS Integration Contract For an existing chat agent, do the simplest thing that can work: 1. Get approved credentials: `CONFIGURE_API_KEY=sk_...`, `CONFIGURE_PUBLISHABLE_KEY=pk_...`, and `CONFIGURE_AGENT=your-agent`. If credentials are missing, run `npx configure setup` and choose **For my users**; if access is pending, stop and wait for approval. Run `npm install configure` if the SDK is not installed. 2. In the browser, load `https://configure.dev/js/configure.js`. Put Configure in the existing chat `+`/attachment menu, and mount Link in the transcript with `linkEl`. The Link host should span the assistant message lane (`width: min(100%, 640px)`) and must not be capped at 420px or mounted inside the composer row. Do not serve the hosted script from localhost for real Link testing. 3. Store the `configure:linked` token on your server. Never put `sk_` keys, Link tokens, or user IDs in browser model prompts. 4. In the chat route, create `const profile = configure.profile({ token })`, pass `profile.tools()` to the model, execute any `configure_*` tool call with `profile.executeTool(toolCall)`, then pass the tool result back to the model before returning the assistant message. 5. Verify one real Configure tool call succeeds and the assistant answers from the result: ask the model to read the user's Configure profile, or headlessly call `profile.executeTool({ name: "configure_profile_read", arguments: { sections: ["identity", "preferences", "imports"] } })`. That is the integration. Avoid scaffolding a new app, adding settings pages, inventing custom memory APIs, or hand-writing Configure tool schemas unless the user explicitly asks. ## Integration Decision Always choose one path before editing: 1. Integrate an existing agent. This is the default when the repo already has a chat UI, model loop, backend route, or tool router. Inspect the app and add Configure in place. 2. Build a new Configure agent. Use the packaged template only when the developer wants a fresh agent shell. If `.env` already contains `CONFIGURE_API_KEY`, `CONFIGURE_PUBLISHABLE_KEY`, and `CONFIGURE_AGENT`, use those values. Do not rerun setup unless they are missing. ## Core Product Loop 1. The host app invokes Configure Link from a user action such as Personalize or Configure. 2. The user federates with a supported identity method. Today this is phone OTP. 3. The user can seed context by connecting supported apps or importing memories. 4. The user scopes what the acting agent can access. 5. Configure returns an agent-scoped token through `configure:linked`, then the Link surface collapses. 6. The backend creates `configure.profile({ token })`. 7. The agent performs a profile read or profile tool call before any personalized response; chat hosts may show `` during this handoff. Keep runtime reading visible for at least 1200ms, but do not hold the response longer than necessary after that minimum. 8. After a read-backed turn, the runtime calls `profile.commit()` with bounded source material or explicit memory candidates. ## Browser Integration Default for chat products: add a Personalization row to the existing `+`/attachment menu next to Images and Files. The `+` entry triggers Configure only; mount Configure Link in a dismissible inline chat transcript panel with `linkEl`. Size that panel like an assistant artifact: `width: min(100%, var(--chat-max, 640px))`, not a 420px settings card and not inside the composer bar. The `onEvent` callback exposes the token at `event.payload.token`: ```html
``` Do not place Configure on a settings page or as a standalone chat-bar button when the product already has a `+` menu or integrations list. Secondary path only (settings page or static button, not the chat default): use the declarative trigger, which exposes the token at `event.detail.token`: ```html ``` If the app has an inline integrations list instead of a `+` menu, use the rounded Configure lockup button: ```js Configure.personalizationButton({ el: "#configure-integration", publishableKey: "pk_...", agent: "your-agent", displayName: "Your Agent", variant: "integration", }); ``` The hosted script is the recommended production path. It mounts secure Configure-owned iframe surfaces, handles inline sizing, opens the normal phone flow in-page, fires `configure:linked` when the user completes consent, and then collapses the Link surface so the host can resume the agent response. `Configure.personalizationButton()` keeps Images and Files host-owned via `onImage`, `onFile`, `configure:image-select`, and `configure:file-select`; use `linkEl`/`inlineEl` when the trigger lives in the composer but Link should appear in the chat transcript. Already-linked personalization changes emit `configure:personalization-toggle` without reopening Link. Do not expose `sk_` keys in browser code. Browser code uses publishable keys only. ### Sign in with Configure Use Sign in with Configure when Configure is the user's auth handoff into an agent or app. Do not implement OAuth/OIDC around it. Let the first-party accounts surface own phone verification and access review, then return only a short-lived code to your app. Use a desktop popup to preserve app context; use a full-page redirect for mobile, popup-blocked browsers, or simpler auth routes. Server setup: 1. Allowlist a callback or native deep link with `POST /v1/auth/sign-in/return-destinations` using `X-API-Key: sk_...`. 2. In browser code, call `Configure.signInWithPopup({ publishableKey: "pk_...", agent: "your-agent", returnTo: "https://app.example/auth/configure/callback", state: "opaque", fallback: "redirect" })`; it opens `accounts.configure.dev` with `delivery=opener` and redirects the current window if the popup is blocked. 3. Exchange the returned `cfgsic_...` code with `POST /v1/auth/sign-in/exchange` using `X-API-Key: sk_...`. 4. Store the returned agent-scoped token in the host app's server session and use `configure.profile({ token })`. Codes are single-use, expire after five minutes, and are scoped to the developer account plus acting agent. Never put the exchanged token in a URL. To skip the popup entirely, redirect to `https://accounts.configure.dev/?pk=pk_...&agent=your-agent&return_to=...&state=...`. When the signed-in user opens inline Configure later, pass the stored token and userId to `Configure.link({ token, userId, ... })` so Configure can skip OTP and go straight to scoping, repair, or tool connection. ## Server SDK ```ts import { Configure } from "configure"; const configure = new Configure({ apiKey: process.env.CONFIGURE_API_KEY, agent: process.env.CONFIGURE_AGENT, }); app.post("/api/configure/session", requireUser, (req, res) => { if (!req.body.token) return res.status(400).json({ error: "missing_token" }); req.session.configureToken = req.body.token; res.sendStatus(204); }); app.post("/api/chat", async (req, res) => { const { messages } = req.body; const token = req.session.configureToken || req.body.token; if (!token) return res.status(401).json({ error: "configure_not_linked" }); const profile = configure.profile({ token }); const read = await profile.read(); const result = await model.run({ messages: [ { role: "system", content: read.profile.format({ guidelines: true }) }, ...messages, ], tools: profile.tools(), executeTool: profile.executeTool, }); await profile.commit({ messages, response: result, }); res.json({ text: result.text }); }); ``` The Link token goes to the backend. The model sees formatted profile context and Configure tools, never raw browser tokens or user IDs. Agent handles are the public acting-agent identifier. They are lowercase letters, numbers, and hyphens, 2-63 characters, start and end alphanumeric, and must not be reserved platform names. The API key is the server-side source of truth for the acting agent on writes. ## Tool Names Default model tools: - `configure_profile_read` - `configure_profile_search` - `configure_profile_remember` Default SDK tools do not include `configure_profile_commit`. `profile.commit()` is runtime plumbing, not an unmanaged general model tool. Package MCP is the adapter exception: it mirrors the same model-facing `configure_*` tool-call pattern and includes `configure_profile_commit` because generic MCP clients usually expose only tool calls. `configure_profile_read` accepts section filters including `imports`. Use `imports` for user-directed ChatGPT/Claude/etc. imported memories. Use `integrations` for connected tools such as Gmail, Calendar, Drive, and Notion. To list one imported source, use `profile.search({ query: "*", source: "chatgpt" })` or `source: "import:chatgpt"`. Connector-backed tools and action tools must be enabled explicitly: ```ts profile.tools({ connectors: ["gmail", "calendar", "drive", "notion"], actions: ["email.send", "calendar.create_event"], }); ``` ## Raw Components Raw web components are included in the npm package for local labs and advanced self-hosted surfaces: ```ts import "configure/components"; ``` Available UI entry and review components include ``, ``, ``, ``, ``, standalone ``, and ``. Prefer `https://configure.dev/js/configure.js` unless the integration intentionally owns the raw component surface. ## Terminology - Use connector for Gmail, Calendar, Drive, Notion, and similar external accounts. - Use tool for model-callable functions. - Use memory import for ChatGPT, Claude, Gemini, and similar export/paste flows. - Use profile read/search for approved profile access. - Use remember for one explicit durable fact. - Use commit for bounded post-turn write-back after profile reads. Do not use raw CFS paths in normal model-facing examples. Raw file APIs are advanced and live outside the default profile loop.