Personalization for agents.
One package, one call. Your agent recognizes the user from the first message — across every channel, monetized automatically.
Two complete, copy-paste examples — each is the whole integration:
| Example | What it shows |
|---|---|
web/ |
A "Continue with Configure" button that redirects, exchanges a code, and reads the profile. |
message-agent/ |
An iMessage agent that recognizes returning users by phone with resolveMessageIdentity. |
npm install configureRequires configure >= 1.1.9.
Web — one personalize() call runs the whole sign-in server (no framework, no routes):
import { personalize } from "configure";
personalize({
apiKey: process.env.CONFIGURE_API_KEY!, // sk_, server-side
publishableKey: process.env.CONFIGURE_PUBLISHABLE_KEY!, // pk_, browser-safe
agent: process.env.CONFIGURE_AGENT!,
baseUrl: "http://localhost:4000",
onSignedIn: ({ profile }) => profile, // return HTML or JSON
}).listen(4000);Message agent — wrap a Spectrum iMessage loop: it recognizes the texter by phone, sends the sign-in link in-thread, and hands you their profile.
import { personalize } from "configure/spectrum";
personalize(app, {
apiKey: process.env.CONFIGURE_API_KEY!,
publishableKey: process.env.CONFIGURE_PUBLISHABLE_KEY!,
agent: process.env.CONFIGURE_AGENT!,
reply: ({ name, linked }) =>
linked ? `hey ${name ?? "there"}, what's up?` : `text "connect" to sign in`,
});
personalizelives at the package root for web; the Spectrum adapter isconfigure/spectrumbecause it pulls inspectrum-ts— so web-only users never install it.
Under the hood, that is four SDK calls — build the link, exchange the code server-side, read the profile:
const configure = new Configure({ apiKey, agent });
const url = configure.auth.signInUrl({ publishableKey, returnTo }); // sign-in.me hosted link (pk_)
const { token } = await configure.auth.exchangeSignInCode(code); // exchange (sk_)
const profile = await configure.profile({ token }).read(); // readThe same sign-in.me hosted link works on the web (a redirect), in iMessage (a text), or in a voice agent (read aloud) — and Configure recognizes the same user across all of them at once. One identity, everywhere.
git clone https://github.com/configure-dev/configure-quickstart
cd configure-quickstart/web
cp .env.example .env # add your keys
npm install
npm run dev # → http://localhost:4000Open the page, click Continue with Configure, and you land on a page showing the profile your server just read. Get your keys with npx configure setup or from the dashboard.
Configure uses two keys with different reach. Getting this split right is the entire security model.
| Variable | Prefix | Where it lives |
|---|---|---|
CONFIGURE_API_KEY |
sk_ |
Server only. Exchanges the code and reads profiles. |
CONFIGURE_PUBLISHABLE_KEY |
pk_ |
Browser-safe. Builds the sign-in link. |
CONFIGURE_AGENT |
— | Your agent handle in Configure. |
The secret key never leaves your server. The browser only ever holds the publishable key and a one-time code; the token and every profile read stay on the backend.
Browser Your server (sk_) Configure (hosted)
─────── ───────────────── ──────────────────
Continue with Configure ───────────▶ /login ──── signInUrl() ──────▶ phone + consent
│
show profile ◀──── profile.read() ◀── exchangeSignInCode(code) ◀────┘ redirect ?code=
In an agent, the redirect collapses into a single message. The agent texts the sign-in.me link, the user signs in once, and resolveMessageIdentity matches them by phone on every turn after that — no second OTP loop in the thread.
This repo is agent-readable. Point a coding agent at llms.txt for the whole integration in one file, or drop SKILL.md into its skills — it will install configure, build the hosted link, exchange the code, and make the first profile read.