Quick Start
This path integrates Configure into an existing web agent. It does not scaffold a new app.
For products with existing sign-in, the default is Continue with Configure + inline Configure in chat. Link-only is the fallback for products without sign-in or an OAuth provider row.
1. Install And Setup
bash
npm install configure
npx configure setup --usersIf you run npx configure setup without flags, choose For my users.
When setup opens the browser, complete Configure developer auth. Create or sign into a developer account, then choose the existing agent handle for the product you already ship or create one if this is 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-agentIf .env already has these three values, reuse them and skip setup.
2. Add Continue With Configure
Add Configure beside the app's existing SSO providers:
html
<script src="https://configure.dev/js/configure.js"></script>
<configure-sso-button
client-id="oc_..."
redirect-uri="https://app.example.com/auth/configure/callback"
scopes="profile.read profile.search profile.remember profile.commit"
width="100%">
</configure-sso-button>Create the OAuth client and implement the callback with the Continue with Configure guide. The callback must exchange the code on your backend and store Configure access and refresh tokens server-side:
ts
app.post("/api/configure/oauth/callback", async (req, res) => {
const { code, codeVerifier, redirectUri } = req.body;
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: redirectUri,
client_id: process.env.CONFIGURE_OAUTH_CLIENT_ID!,
code_verifier: codeVerifier,
resource: "https://api.configure.dev",
}),
});
const tokens = await tokenRes.json();
if (!tokenRes.ok) {
res.status(400).json({ error: "configure_oauth_failed" });
return;
}
req.session.configureAccessToken = tokens.access_token;
req.session.configureRefreshToken = tokens.refresh_token;
res.json({ connected: true });
});Never expose client_secret, sk_ keys, OAuth access tokens, or OAuth refresh tokens in browser JavaScript.
3. Add Inline Configure In Chat
Configure should exist inside the chat, either in the same + menu as Images and Files or in an integrations list. For users who completed Continue with Configure, this surface should manage profile permissions, connected tools, and personalization settings instead of asking them to authenticate again.
html
<script src="https://configure.dev/js/configure.js"></script>
<div id="configure-entry"></div>
<script>
Configure.personalizationButton({
el: "#configure-entry",
publishableKey: "pk_...",
agent: "your-agent",
agentName: "Your Agent",
personalizationLabel: "Manage Configure",
variant: "integration",
onEvent(event) {
if (event.type === "configure:linked") {
fetch("/api/configure/session", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
token: event.payload.token,
userId: event.payload.userId,
}),
});
}
},
});
</script>For SSO-authenticated users, keep OAuth tokens on the backend and let hosted Configure UI reuse the Configure-origin session. Pass a browser-safe Configure handoff token or userId only when your backend intentionally returns one for hosted UI. Do not pass OAuth access or refresh tokens into the browser.
If your product is not a chat UI, use a settings-page Link trigger instead. See Inline UI Components.
4. Store Link Fallback Tokens
The inline surface emits configure:linked when the user completes Configure Link. This is the fallback path for products without SSO and a repair path for users who need to re-link. Send the returned token to your backend and store it in the existing app session.
ts
app.post("/api/configure/session", requireUser, (req, res) => {
const { token, userId } = req.body;
if (!token) {
res.status(400).json({ error: "missing_token" });
return;
}
req.session.configureToken = token;
req.session.configureUserId = userId;
res.sendStatus(204);
});The token is an agent-scoped Configure token. Do not put it in the model prompt.
5. Wire Configure Into Your Chat Route
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 token = req.session.configureAccessToken || req.session.configureToken;
if (!token) {
res.status(401).json({ error: "configure_not_connected" });
return;
}
const profile = configure.profile({ 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. When the model
// calls configure_profile_read, that tool call is the personalization magic moment.
// Concrete OpenAI and Anthropic loops: /guides/tool-calling
const response = await runYourModelLoop({ messages, tools, dispatch });
// After a read-backed turn, write bounded runtime memory in the background.
await profile.commit({
messages,
response,
memories: response.memoryCandidates,
});
res.json({ text: response.text });
});Letting the model call configure_profile_read is the magic moment, and it produces a real tool call you can see in your logs. Use profile.search() for attributed retrieval, profile.remember() for one explicit fact or preference, and profile.commit() after a read-backed turn so Configure can write bounded runtime memory.
You can also pre-read on the server for prompt assembly or latency:
ts
const read = await profile.read({ sections: ["identity", "preferences", "summary"] });
// prepend read.profile.format({ guidelines: true }) to your system promptA server-side pre-read is an advanced pattern. It does not count as the model's first tool call. Keep configure_profile_read in profile.tools() so the first observable success still happens inside the model loop.
6. Verify The First Tool Call
Three success events are easy to confuse. Track them separately:
Configure SSO connected- the user completed Continue with Configure and your backend stored server-side tokens.configure:linked- the user completed inline Configure Link and your backend stored a fallback token.configure_profile_readviaprofile.executeTool()- the model or smoke test made a Configure tool call. This is the personalization success moment.
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.
7. Show Runtime Reading
After SSO or configure:linked, chat hosts may show <configure-runtime-reading> while the first backend profile read and response are in flight. Keep it visible for at least 1200ms, then remove it as soon as the agent response is ready.
html
<configure-runtime-reading
agent-name="Your Agent"
message="Reading approved profile">
</configure-runtime-reading>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.