Components
The Quickstart uses the simplest entry point: your own chip, linked to a route on your server that sends the user to Configure's page. Use the components below when you want Configure's page inside yours instead. Configure serves them all, and all of them run in production. Two scripts carry them: https://configure.dev/js/configure.js gives you the Configure.* functions, and https://configure.dev/components/configure-components.global.js defines the raw elements. Include each once per page.
publishableKey is your pk_ key, safe in the browser, and agent is your agent handle. Both come from npx configure setup --users. displayName is what the user reads; agent is the identity every approval and token is bound to.
Composer button
The entry point for a chat. It sits next to the image and file attachments as a + menu, with a Personalization row that opens Configure's hosted flow inline. Pass linkEl so the flow mounts in a dismissible panel across the assistant lane instead of inside the composer row.
html
<script src="https://configure.dev/js/configure.js"></script>
<div id="configure-entry"></div>
<div id="configure-link-host"></div>
<script>
Configure.personalizationButton({
el: "#configure-entry",
linkEl: "#configure-link-host",
publishableKey: "pk_...",
agent: "your-agent",
displayName: "Your App",
theme: "light", // or "dark"; omit it and the script warns and follows the system
onImage: () => openImagePicker(),
onFile: () => openFilePicker(),
});
</script>Let the link host span the assistant message lane. Do not cap it at 420px; the flow needs the width.
Your own button
Most products already have a button style, so the entry point is your own control. It links to the route that sends the user to Configure's page, and Configure brings them back to your return route. This is the Quickstart pattern, steps 2 through 5.

html
<a class="chip" href="/configure/start">Connect</a>The route mints the link with a returnUrl and redirects. profileFor is the handle from the Quickstart, and RETURN_URL is your return route's https address, a constant:
ts
const RETURN_URL = "https://yourapp.com/configure/return";
app.get("/configure/start", async (req, res) => {
const connect = await profileFor(req.user.id).connect({ returnUrl: RETURN_URL });
res.redirect(connect.connect_url); // Configure's page, then back to RETURN_URL
});Configure brings the user back to your return route: Quickstart, step 5.
Integration button
For a settings or integrations surface, the raw element renders a connect or manage control that opens Configure's hosted flow itself.
html
<script src="https://configure.dev/js/configure.js"></script>
<script src="https://configure.dev/components/configure-components.global.js"></script>
<configure-personalization-button
variant="integration"
external-id="user_123"
></configure-personalization-button>variant is menu, the composer button above as a raw element, or integration. With manual the element announces its events and never opens the flow, which is how the demos on this page run.
Hosted link
When you want the flow on its own, mount it and take the token from the event. This is the same grant the composer button and the integration button run; here your page owns where it renders.
html
<script src="https://configure.dev/js/configure.js"></script>
<div id="configure-link"></div>
<script>
Configure.link({
el: "#configure-link",
publishableKey: "pk_...",
agent: "your-agent",
displayName: "Your App",
theme: "light",
onEvent: (event) => {
if (event.type === "configure:linked") {
// event.payload = { token, userId, tokenUse }
sendTokenToYourBackend(event.payload.token);
}
},
});
</script>Configure.link() returns a frame handle, not the token. The token arrives in configure:linked, through onEvent or as a DOM CustomEvent on the mount element. Send it to your server and use it there with configure.profile({ token }).
Connected
The composer button flips its own state when it sees the link complete on the page. Across reloads, and for your own button, render the connected state from what your server already knows: the context your daily refresh stored.
ts
// The refresh in Quickstart step 6 stores { text, refreshedAt }. text is null until the user connects.
const stored = await store.get(user.id);
const label = stored?.text ? "Connected" : "Connect";Render from what Configure told you, stored and refreshed on the daily read. That keeps page loads off the network. Never set a "connected" flag from your own events.
Approval card
For the two tools that change the outside world, sending an email and creating an event, the bundle ships a card that asks the user first. It shows the tool and its parameters, times out to Deny, and answers with one event.
html
<configure-tool-approval
action-id="call_123"
tool="configure_email_send"
timeout-seconds="60"
></configure-tool-approval>
<script>
const card = document.querySelector("configure-tool-approval");
card.params = { to: "sam@example.com", subject: "Intro" }; // a property, not an attribute
card.addEventListener("configure:tool-approve", (e) => runTool(e.detail.actionId));
card.addEventListener("configure:tool-deny", (e) => skipTool(e.detail.actionId));
</script>configure:tool-always-allow fires when the user chooses to stop being asked for that tool; remembering that choice is yours.
Attributes
On the raw <configure-personalization-button>:
| Attribute | Default | What it does |
|---|---|---|
variant | menu | menu or integration |
external-id | Your user id, so the connection lands on the profile your server reads | |
connectors | Which apps the flow offers, comma separated | |
personalization-label | The text of the Personalization row | |
placeholder | Placeholder text in the composer slot | |
manual | false | Announce events only; never open the flow |
hide-images, hide-files | false | Remove the image or file rows from the + menu |
image-label, file-label | Text of the image and file rows | |
max-visible | How many recent items the menu lists | |
show-actions, show-settings | Show the actions row and the settings row |
Events the element announces: configure:personalization-open and configure:personalization-toggle as the flow opens and closes, configure:personalization-menu-toggle for the + menu, configure:linked when the link completes, configure:image-select and configure:file-select for the attachment rows, and configure:settings-press.
The buttons are real buttons: keyboard reachable, with a visible focus ring, and still on the page at 375px wide.