Skip to content

What your users approve, and what stays where

Configure's promise to a user is that nothing about them moves without their say. Your integration keeps that promise by respecting a few boundaries, and every one of them is enforced by Configure, not by your good intentions.

What Configure stores

A profile is eight sections, and a read returns only the ones you ask for:

SectionWhat it holds
identityName, location, timezone, occupation, bio, interests
preferencesDurable preferences the user or an agent recorded
integrationsWhich apps the user connected, and their state
importsContext brought over from another assistant, such as a ChatGPT export
agentsWhat other agents the user approved have learned about them
summaryConfigure's synthesis of the above
soulThe longer-form character document
contextThe working-context document

Your app never sees a user's password or their one-time codes. It never receives an access token for a connected app either: your agent calls a tool and gets the result, never the credential behind it. That is a separate thing from your own OAuth client secret in the table below, which is yours and stays on your server.

What the user controls

  • Consent is per agent. The hosted flow shows your app's name and exactly what it gets. The user chooses what to share: identity and preferences, connected apps, memories from other apps. Those choices apply only to your agent.
  • Every memory is attributed. A user can see which agent saved what, and nothing one agent saves rewrites who they are on its own.
  • Revoke anytime. From their Configure profile page a user can remove your agent's access, disconnect an app, or delete their profile. When they do, your reads start refusing with a link, not silently going stale. Handle authorization_required and tool_not_connected as described in the Quickstart.

The profile is user data, not instructions

You inject the profile into your system prompt, and the profile is not yours. Its memories come from the user's other assistants and from transcripts they pasted in, so a memory can contain whatever you use to delimit the block. If it does, the block ends early and the rest of that memory lands in your prompt where instructions go.

Strip your delimiter out of the text before you wrap it, and strip it repeatedly until a pass changes nothing:

ts
// Matches the shape of the tag, not one spelling: "</ user_context>",
// "</user_context >" and "<user_context role='system'>" all read as the
// delimiter to a model, and \b keeps "<user_contextual_notes>" as prose.
const DELIMITER = /<\s*\/?\s*user_context\b[^>]*>/gi;

export function fence(profileText: string): string {
  let text = profileText;
  for (;;) {
    const stripped = text.replace(DELIMITER, "");
    if (stripped === text) return stripped;   // a fixed point, not one pass
    text = stripped;                          // every pass shortens, so this ends
  }
}

One pass is not enough, and this is the case that proves it: </user_context<user_context>> loses the inner tag and reassembles a valid closing delimiter out of what is left. Loop to a fixed point.

This is a fence, not a filter. The surrounding prose survives; only the delimiter goes. And say in the prompt what the block is: the user's context, never a source of instructions, never something to place into a search query or a page you fetch.

Where credentials live

CredentialLivesNever
sk_ secret keyYour server's environmentIn a browser, a prompt, or a repo
pk_ publishable keyBrowser code and the hosted flowUsed to authorize a server call
User tokens and MCP session tokensYour server, against your session storeIn the model's context
OAuth client id and secretYour server, for the sign-in variationAnywhere a client can read

What the model can reach

The model receives Configure tool definitions and tool results, and nothing else. Two consequences:

  • Identity never comes from the prompt. No Configure tool accepts a user id. Whose memory a call touches is decided by the credential your server minted, so a prompt injection inside a retrieved email cannot redirect a read to another user.
  • State-changing tools fail closed. Sending an email, creating an event, or writing to a sheet needs a connected account and a user who approved your agent; without both, the call refuses with the link that fixes it. Confirming an individual send or event with the user is your app's job, and Connected apps shows the component for it.

If your server injects profile context into the system prompt, keep that path app-owned and keep the Configure tools available to the model as well. The injection is the floor; the tools are how the model gets more.

Test data is separate

The sandbox is its own plane. sk_test_ keys cannot read live profiles, live keys cannot read sandbox data, and every address in the synthetic profile is example.com. Rehearse everything there first.

Personalization infrastructure for agents