Quick Start
This path integrates Configure into an existing web agent. What you are building is the loop: a user signs in with Configure once, and from then on your agent reads their profile at the start of every conversation, uses the profile tools during turns, and writes back what it learned after. A rendered sign-in button is not the integration; the loop is.
All docs pages, indexed for agents: https://docs.configure.dev/llms.txt (add .md to any page URL for plain markdown).
Rather not do these steps by hand? Set up with your coding agent is the one-paste version of this page: your coding agent runs it end to end.
If your agent already speaks MCP, stop here: that is the ideal integration. Connect it to Configure MCP (remote clients paste https://mcp.configure.dev; code uses https://mcp.configure.dev/mcp) and the configure_* tools appear natively, sign-in included. Continue below when you own the app UI, auth flow, and model loop. Not sure which you are? Choose your integration.
1. Get the five credentials
bash
npm install configure
npx configure setup --usersIf you run npx configure setup without flags, choose For my users. To skip the CLI entirely, sign in at configure.dev/login and copy the five values off the keys screen instead (Get your credentials); the rest of this page is the same either way.
No account yet, and you want to see it work first? curl -X POST https://api.configure.dev/v1/sandbox/provision returns a sk_test_/pk_test_ pair and a synthetic user whose profile is already full, and every step below works with those keys (Test mode (sandbox)).
When setup opens the browser, sign in to (or create) a developer account, then choose the existing agent handle for the product you already ship, or create one for a new agent. Creating the API key is a credential step, not a decision to build a new app. Setup writes:
bash
CONFIGURE_API_KEY=sk_...
CONFIGURE_PUBLISHABLE_KEY=pk_...
CONFIGURE_AGENT=your-agent
CONFIGURE_OAUTH_CLIENT_ID=oc_...
CONFIGURE_OAUTH_CLIENT_SECRET=ocs_...That is every credential the rest of this page needs. Setup registers the callback http://localhost:3000/auth/configure/callback by default. If your app runs somewhere else, pass it:
bash
npx configure setup --users --redirect-uri http://localhost:5173/auth/configure/callbackSetup registers loopback callbacks only (http://localhost or http://127.0.0.1), because the browser step that mints the client is reachable before you are signed in. When you ship, add the deployed callback with npx configure add origin https://yourapp.com/auth/configure/callback, or with Add callback on the dashboard's Sign-in (SSO) page. Both keep the same CONFIGURE_OAUTH_CLIENT_ID. The redirect URI must match exactly at sign-in time, so set the local one now, not after the first failed login.
If .env already has all five values, reuse them and skip setup. If it has the three keys but no CONFIGURE_OAUTH_CLIENT_ID, rerun setup: it registers the OAuth client and leaves the keys as they are. Rerunning against a callback that already has a client reuses that client and issues it a fresh secret, so update CONFIGURE_OAUTH_CLIENT_SECRET anywhere else you deployed it.
The CLI's full command surface is machine-readable: npx configure guide prints every command, flag, and the exit-code contract as JSON (0 success, 1 error, 2 auth, 3 not found, 4 usage).
2. Sign in with Configure
Add Configure beside the app's existing sign-in providers. client-id is the CONFIGURE_OAUTH_CLIENT_ID from step 1, and redirect-uri must be the callback step 1 registered, character for character:
html
<script src="https://configure.dev/js/configure.js"></script>
<configure-sso-button
client-id="oc_..."
redirect-uri="http://localhost:3000/auth/configure/callback"
agent-name="Your Agent"
scopes="profile.read profile.search profile.remember profile.commit"
width="100%">
</configure-sso-button>Sign-in needs two routes at two different URLs. Both are required, and only the first one is registered:
| Route | URL | Job |
|---|---|---|
| Callback page, in the browser | The redirect URI you registered in step 1, http://localhost:3000/auth/configure/callback | Reads code and state off the URL, looks up the PKCE verifier the button stored, posts both to your backend, then calls Configure.completeSso() to close the popup. |
| Exchange endpoint, on your backend | POST /api/auth/configure/callback | Trades the code for tokens using your client secret and stores them server-side. Never registered as a redirect URI, and never reached by the browser's redirect. |
The verifier lives in the browser because the button put it there, and the client secret lives on your server. That is why the flow needs both halves. Generate them:
bash
npx configure add callback --framework next|express|viteIt writes the callback page and the exchange endpoint for the callback you registered, and derives the backend URL from it (/auth/configure/callback becomes /api/auth/configure/callback). For next it also writes a ConfigureSignInButton component. The generated pair already handles the popup handoff, the single-use code, and PKCE recovery; the production checklist explains why a hand-written callback usually misses them.
Writing it by hand instead, the browser half is:
js
const params = new URLSearchParams(window.location.search);
const state = params.get("state");
const pkce = Configure.getSsoPkce(state);
const res = await fetch("/api/auth/configure/callback", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ code: params.get("code"), state, codeVerifier: pkce.codeVerifier }),
});
Configure.completeSso({ state, payload: await res.json() });And the backend half exchanges the code with your secret, using the registered redirect URI as a constant rather than one rebuilt from window.location:
ts
const tokenRes = await fetch("https://api.configure.dev/oauth/token", {
method: "POST",
headers: {
"Authorization": "Basic " + Buffer.from(
process.env.CONFIGURE_OAUTH_CLIENT_ID + ":" +
process.env.CONFIGURE_OAUTH_CLIENT_SECRET
).toString("base64"),
"Content-Type": "application/x-www-form-urlencoded",
},
body: new URLSearchParams({
grant_type: "authorization_code",
code,
redirect_uri: "http://localhost:3000/auth/configure/callback",
code_verifier: codeVerifier,
}),
});
const tokens = await tokenRes.json();
await saveConfigureTokens(req.session.userId, tokens);Keep the tokens server-side. The full version, with error paths and the missing-state recovery, is in Handle the callback.
Sign-in is the door, not the destination. The user has now handed your product their context; the next step is the part users actually feel.
3. Run the loop in your chat route
This is the integration. Read the profile when a conversation starts, keep the profile tools in the model's hands for the whole turn, and commit after the turn:
ts
import { Configure } from "configure";
const configure = new Configure({
apiKey: process.env.CONFIGURE_API_KEY,
agent: process.env.CONFIGURE_AGENT,
});
app.post("/api/chat", async (req, res) => {
const { messages } = req.body;
const tokens = await loadConfigureTokens(req.session.userId);
const profile = configure.profile({ token: tokens.access_token });
// Expose Configure tools alongside your own. profile.tools() returns
// Anthropic-native { name, description, input_schema }: pass it straight to the
// Anthropic SDK, or wrap with toOpenAIFunctions() (exported from "configure") for OpenAI.
const tools = [
...yourTools,
...profile.tools({
connectors: ["gmail", "calendar"],
}),
];
// In your existing tool dispatch, route Configure-prefixed calls to Configure.
// executeTool accepts { name, arguments } (OpenAI) or { name, input } (Anthropic).
const dispatch = (toolCall) =>
toolCall.name.startsWith("configure_")
? profile.executeTool(toolCall)
: executeYourTool(toolCall);
// Run your existing provider loop with these tools + dispatch. A successful
// configure_profile_read call is the clearest smoke test for your tool router.
// Concrete OpenAI and Anthropic loops: /guides/tool-calling
const response = await runYourModelLoop({ messages, tools, dispatch });
// Close the loop: after a read-backed turn, write bounded memory back.
// commit derives memories from `messages` server-side; pass `memories`
// only when you have explicit facts to save, e.g. ["Prefers metric units"].
await profile.commit({
messages,
response,
});
res.json({ text: response.text });
});Building on the Vercel AI SDK? Use toAISDKTools() from configure/ai-sdk instead of spreading profile.tools(), and see Configure with the Vercel AI SDK for the complete route handler.
The habits that make this the loop rather than a one-off call: configure_profile_read once at the start of each conversation, profile.search() / configure_profile_search for concrete memories and source-specific questions during the turn, profile.remember() the moment the user states a durable fact, and profile.commit() after the turn so the user's next conversation, anywhere, already knows what this one learned. Every call runs securely through the Configure server, gated by the permissions the user granted at sign-in. The loop covers the failure cases: authorization challenges, refusals, and what never to save.
If your app deliberately owns a preloaded approved context slot, choose the sections it needs. Valid sections are identity, preferences, integrations, imports, agents, summary, soul, and context:
ts
const read = await profile.read({ sections: ["identity", "preferences", "summary"] });
const approvedContext = read.profile.format({ guidelines: false });Keep configure_profile_read and configure_profile_search in profile.tools() so the model can refresh the overview or retrieve specific memories during the turn.
4. Prove it
Before the model loop, prove the credentials themselves:
bash
npx configure verifyOn success it prints five green checks and the next command:
text
✓ .env has the five credentials all five present
✓ secret key is accepted resolves to an agent
✓ OAuth client and callback are registered http://localhost:3000/auth/configure/callback accepted
✓ client secret exchanges the code for tokens access token issued
✓ configure_profile_read returns a profile read succeeded with the OAuth access token
Everything the golden path needs works.
Next: add the sign-in button and the callback route with
npx configure add callback.Verify completes a real Configure sign-in, exchanges the code with your client secret, and reads a profile with the resulting token. A rotated secret or a callback that differs by one character fails in your terminal, with the command that fixes it. Stop your dev server first (verify serves the callback while it waits), or run npx configure verify --offline to check credentials and registration only.
To verify the tool path without a model, execute the read tool directly:
ts
const result = await profile.executeTool({
name: "configure_profile_read",
arguments: { sections: ["identity", "summary", "preferences", "imports"] },
});A non-error result confirms your keys, agent handle, token, and tool path are wired correctly. Done means a real turn: the model calls configure_profile_read, the reply uses the returned profile, and commit runs after, not a button that renders.
Track these success events separately:
Configure SSO connected- the user completed Configure OAuth and your backend stored server-side tokens.configure_profile_readviaprofile.executeTool()- the model or smoke test made a Configure tool call.configure:linked- the user completed inline Configure Link (the fallback below) and your backend stored a fallback token.
Product surfaces and fallbacks
These are optional surfaces around the loop, not steps of it:
- Permissions and connections management: after sign-in, give users a place to manage what your agent sees: the hosted personalization entry point mounts in a settings page or an integrations list (Inline UI Components). Users who completed Configure OAuth are never asked to authenticate again there.
- Link fallback: for products with no sign-in flow at all, Configure Link connects a profile without SSO. The inline UI emits
configure:linked; send the returned token to your backend and store it in the existing app session; it is an agent-scoped Configure token, and it never goes in the model prompt (Reference: auth). - Reading state: chat apps may show
<configure-runtime-reading agent-name="Your Agent" message="Reading approved profile">while the first profile read is in flight; keep it visible at least 1200ms.
App-local users
If a user has not linked or signed in with Configure yet but your product has a stable user ID, use an app-local unlinked profile on the server:
ts
const profile = configure.profile({ externalId: "customer-123" });Unlinked profiles are developer-scoped. They are not federated across developers until the user links through Configure Link or signs in with Configure.