Skip to content

Auth Flows

Server-side snippets on this page use a constructed client:

ts
import { Configure } from "configure";

// Falls back to CONFIGURE_API_KEY / CONFIGURE_AGENT env vars.
const configure = new Configure({ apiKey: "sk_...", agent: "your-agent" });

The agent handle is mandatory: the Configure constructor throws without one. Pass agent or set CONFIGURE_AGENT. See Configure Reference for constructor details.

Configure supports browser and message-agent auth flows. For products with existing sign-in, use Continue with Configure as the OAuth SSO provider and then place inline Configure inside chat. For products without sign-in, use Configure Link as the lower-friction fallback. For agents that live in iMessage, SMS, or another message channel, use sign-in.me.

Configure Link handles user-present memory consent. It covers phone OTP, profile seeding, access review, and agent token minting inside a secure Configure-owned iframe. Connector and import setup happen on Configure's origin; the parent page receives only the final agent-scoped token.

Continue with Configure

Use Continue with Configure when your app has sign-in options or an account-linking flow:

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>

The callback exchanges the authorization code on the backend and stores Configure OAuth tokens server-side. See Continue with Configure for client creation, PKCE, callback, and token exchange details.

Never expose OAuth access tokens, refresh tokens, client_secret, or sk_ keys in browser JavaScript.

Message-Agent SSO

Use the sign_in_url returned by the agent API when the user starts in a message thread. Send it unchanged:

ts
await message.reply(`Connect Configure: ${configure.auth.signInUrl()}`);

Configure hosts phone verification, consent, and connector setup. Multiple Gmail accounts work by default. Message completion is the default; hosted metadata may add an E.164 return number for a direct Messages button. Browser products use Continue with Configure, or trusted metadata can select redirect with an HTTPS agent URL. Developers do not pass branding, credentials, phone numbers, or delivery behavior in the URL.

Use createMessageSignInUrl() only for advanced provider-signed sender binding or thread-specific completion webhooks. See Message-Agent SSO.

If a message user needs to refresh connected apps later, create a message URL with reason: "reconnect" and the affected connectors. When sender proof is unavailable or unsupported, Configure returns a plain hosted reconnect URL such as https://sign-in.me/{agent}/reconnect?connectors=gmail. The {agent} path segment is the stable agent handle, not the display name. The reconnect route preserves the same hosted message defaults as sign-in.me/{agent}; do not send message users to profile.configure.dev for agent-specific connector repair.

On each inbound message, resolve a linked token when one is available and fall back to a developer-scoped externalId otherwise:

ts
const subjectKey = message.sender?.id || space.id;
const stored = await store.get(subjectKey);

const identity = await configure.auth.resolveMessageIdentity({
  externalId: `sms:${subjectKey}`,
  token: stored?.configureToken,
  phoneCandidates: [
    message.sender?.phone,
    message.sender?.address,
    message.sender?.id,
    space.phone,
  ].filter((value): value is string => Boolean(value)),
});

const profile = configure.profile(identity);

Pass the resolved identity straight to configure.profile(). A linked result carries token (linked: true). An unlinked result carries only the developer-scoped externalId (linked: false, source: "external_id"), and the profile call falls back to it. Do not skip unlinked senders. By default a stored token is accepted without validation; pass validateToken: true to validate it first.

See Message-Agent SSO for message channels, completion webhooks, and Better Auth account-linking examples. Better Auth OAuth providers can use configureBetterAuthOAuthProvider() with Better Auth's Generic OAuth plugin.

Attach Link to an existing app button with data-configure-link. Triggered Link opens an in-page hosted flow and does not mount an iframe inside the button.

html
<script src="https://configure.dev/js/configure.js"></script>
<button
  data-configure-link
  data-publishable-key="pk_..."
  data-agent="your-agent">
  Personalize
</button>

<script>
  document.addEventListener("configure:linked", (event) => {
    fetch("/api/configure/session", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ token: event.detail.token }),
    });
  });
</script>

For chat inputs, use the hosted Personalization entry point when you want Configure beside your app's Images and Files actions. If the user already completed Continue with Configure, this entry should open management controls for permissions, connectors, and personalization rather than a second sign-in step.

js
Configure.personalizationButton({
  el: "#chat-entry",
  publishableKey: "pk_...",
  agent: "your-agent",
  displayName: "Your Agent",
  theme: "light",
  font: "Inter, -apple-system, BlinkMacSystemFont, sans-serif",
  onImage: () => openImagePicker(),
  onFile: () => openFilePicker(),
  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 }),
      });
    }
  },
});

Images and Files remain owned by your app. The helper also emits configure:image-select, configure:file-select, configure:personalization-open, and configure:personalization-toggle for apps that want to route those actions themselves.

For settings or onboarding pages, mount Link inline into an explicit container:

html
<div id="configure-link"></div>
<script>
  Configure.link({
    el: "#configure-link",
    publishableKey: "pk_...",
    agent: "your-agent",
    presentation: "inline",
    theme: "light",
  });
</script>

Options

NameTypeRequiredDescription
elstring | ElementNoContainer for inline presentation. Omit for modal/triggered Link.
publishableKeystringYesPublishable key (pk_...) safe for client-side use.
agentstringYesPublic agent handle receiving approved access.
theme'light' | 'dark'NoHosted iframe theme. Pass it explicitly to avoid OS-theme mismatch with the host app.
presentation'modal' | 'inline'NoPublic presentation mode. Defaults to modal for triggers/no el, inline for non-interactive containers.
externalIdstringNoOptional app-local user hint for later reconciliation. Does not authorize portable reads.
tokenstringNoOptional browser-safe Configure handoff or Link token for hosted UI. Never pass OAuth access or refresh tokens.
userIdstringNoOptional Configure user ID hint for hosted UI state. It does not authorize profile reads by itself.
loginHintstringNoOptional phone hint to prefill the supported phone flow.
methods['phone']NoOptional method constraint. Phone OTP is the v1 default.

Events

EventDetailDescription
configure:linked{ token, userId, tokenUse, approved, agent }Fired after successful linking. token is agent-scoped.
configure:error{ code, message }Fired on auth failure.

The parent page never receives the user-scoped OTP JWT. New integrations should listen for configure:linked.

DOM listeners receive this data on event.detail. The onEvent callback receives the same data on event.payload.

Backend step

Auth flows do not put profile data into the model context. After Continue with Configure, use the server-side OAuth access token. After configure:linked, send the agent-scoped token to your backend. Then expose Configure tools in your model loop and commit after read-backed turns.

ts
const profile = configure.profile({ token });
const tools = [
  ...yourTools,
  ...profile.tools(),
];

const response = await model.run({
  messages,
  tools,
  executeTool: profile.executeTool,
});

await profile.commit({ messages, response });

Follow-on UI

After Link, your app can launch focused hosted UI for repair and enrichment moments:

js
Configure.connections({ presentation: "modal", publishableKey: "pk_...", agent: "your-agent" });
Configure.singleConnector({ presentation: "modal", publishableKey: "pk_...", agent: "your-agent", tool: "gmail" });
Configure.memoryImport({ presentation: "modal", publishableKey: "pk_...", agent: "your-agent", providers: "chatgpt,claude,gemini,grok" });
Configure.profileEditor({ presentation: "modal", publishableKey: "pk_...", agent: "your-agent" });
Configure.accessRequest({ presentation: "modal", publishableKey: "pk_...", agent: "your-agent", tool: "calendar" });

providers, connectors, and tools options accept an array or a comma-separated string.

Focused UI reuses the Configure-origin session created by Continue with Configure or Link. If that session is unavailable, launch Configure.link() first and then open the repair UI.

SDK Methods

WARNING

For production browser linking, use Configure.link() instead of calling these directly. These methods are documented for trusted server-side or headless contexts only.

sendOtp

typescript
auth.sendOtp(phone: string): Promise<OtpStartResponse>

Send a one-time password to a phone number via SMS.

verifyOtp

typescript
auth.verifyOtp(
  phone: string,
  code: string,
  options?: { externalId?: string; embed?: boolean }
): Promise<OtpVerifyResponse>

Verify a 6-digit OTP code. Returns a JWT token and user ID on success for trusted server/headless contexts. options.externalId links an app-local user ID during verification. options.embed marks an embedded flow and returns an embed receipt.

signInUrl

typescript
auth.signInUrl(options?: SignInUrlOptions): string

Builds a plain hosted sign-in URL for sign-in.me, account-link callbacks, browser/deep-link flows, or fallback message handoffs. options defaults to {}; a bare signInUrl() returns the canonical browser-default https://sign-in.me/{agent} URL. See SignInUrlOptions for all fields, including returnTo, state, delivery, messageLinePhone, messageCompleteUrl, and connectors. For a message handoff, set message delivery explicitly:

ts
const url = configure.auth.signInUrl({
  delivery: "message",
  messageLinePhone: "+14155551234",
});

Hosted completion associates the sender back through phone recognition (recognizePhone/phoneCandidates) or a completion webhook. Message-aware agents should use createMessageSignInUrl() when Configure should manage message return metadata and code-bearing link eligibility.

createMessageSignInUrl

typescript
auth.createMessageSignInUrl(options: CreateMessageSignInUrlOptions): Promise<CreateMessageSignInUrlResult>

Creates a hosted sign-in, reconnect, or permission-review URL for a message-channel sender. Requires a server-side sk_ key. The result mode is a two-value union: "minted" or "plain". Code-bearing mode: "minted" results (with code and expiresAt) are returned only when Configure can verify provider-signed messageSenderProof; otherwise the result is mode: "plain" with a fallbackReason and the normal hosted fallback (https://sign-in.me/{agent} for sign-in, https://sign-in.me/{agent}/reconnect for reconnect). See CreateMessageSignInUrlResult.

Current behavior

Provider-signed messageSenderProof is accepted but not yet verified. The endpoint currently always returns mode: "plain" with a fallbackReason (sender_proof_unsupported when proof is present, sender_proof_missing otherwise). Do not build on mode: "minted" yet.

registerMessageLine

typescript
auth.registerMessageLine(options: RegisterMessageLineOptions): Promise<RegisterMessageLineResult>

Registers an agent-owned SMS/iMessage return line before using it in message sign-in URLs. Requires a server-side sk_ key. Configure stores only a hash and last4; SDK responses never include the raw phone number. Put provider return phones in phone / messageLinePhone, not metadata. Non-secret registration metadata is returned for operator/provider debugging; raw phone fields, E.164 values, and NANP-style digit-only phone values are rejected.

listMessageLines

typescript
auth.listMessageLines(): Promise<ListMessageLinesResult>

Lists active message return lines for the acting agent without exposing raw phone numbers. Returned metadata is the non-secret operator metadata supplied during registration.

revokeMessageLine

typescript
auth.revokeMessageLine(options: RevokeMessageLineOptions): Promise<RevokeMessageLineResult>

Revokes an agent-owned message return line. Future message URL requests that include that phone are rejected until the line is registered again.

resolveMessageIdentity

typescript
auth.resolveMessageIdentity(options: MessageIdentityResolveOptions): Promise<MessageIdentity>

Resolves a message sender to a linked Configure token when possible, otherwise returns the provided externalId fallback.

exchangeSignInCode

typescript
auth.exchangeSignInCode(code: string): Promise<SignInExchangeResult>

Exchanges a hosted return-code callback for an agent-scoped token. Requires a server-side sk_ key.

validateSignInToken

typescript
auth.validateSignInToken(token: string): Promise<SignInTokenValidationResult>

Validates an agent-scoped token for the acting agent. Use this before accepting message completion callbacks.

recognizePhone

typescript
auth.recognizePhone(candidates: string | string[]): Promise<SignInPhoneRecognitionResult>

Recognizes phone-like message sender IDs and exact OAuth-verified email-form iMessage handles for already approved users. Email recognition never uses inferred or user-typed profile email. The method name is retained for compatibility. Requires a server-side sk_ key.

allowSignInReturnTo

typescript
auth.allowSignInReturnTo(returnTo: string): Promise<SignInReturnDestinationResult>

Allowlists a hosted return-code callback or deep link for the acting agent. Requires a server-side sk_ key.

listSignInReturnDestinations

typescript
auth.listSignInReturnDestinations(): Promise<SignInReturnDestinationList>

Lists allowlisted hosted sign-in return destinations for the acting agent. Requires a server-side sk_ key.

removeSignInReturnTo

typescript
auth.removeSignInReturnTo(returnTo: string): Promise<SignInReturnDestinationResult>

Removes an allowlisted hosted sign-in return destination. Requires a server-side sk_ key.

configureBetterAuthOAuthProvider

typescript
configureBetterAuthOAuthProvider(options: ConfigureBetterAuthOAuthProviderOptions): ConfigureBetterAuthOAuthProvider

Builds a Better Auth Generic OAuth provider config with Configure defaults for PKCE, client_secret_basic, the API resource, and server-side profile identity lookup. See Message-Agent SSO.

Personalization infrastructure for agents