Skip to content

What your users approve, and what stays where ​

Configure promises each user that nothing about them moves without their say. Your integration keeps that promise by respecting a few boundaries. Configure enforces every one of them.

What Configure stores ​

A profile has eight sections. 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. Your own OAuth client secret, in the table below, is a separate thing. It is yours and stays on your server.

What the user controls ​

  • Consent is per agent. The hosted flow shows your app's name in its title. It has no permissions screen. Finishing the flow approves your agent, and only your agent.
  • Control is on their profile. At profile.configure.dev, a user can narrow what your agent reads (their info, imported memories, what other platforms saved). They can also revoke your agent outright, disconnect an app, or delete their profile.
  • Every memory is attributed. A user can see which agent saved what. Nothing one agent saves rewrites who they are on its own.
  • Revoke anytime. When a user revokes your agent or disconnects an app, your reads are refused with a link. They do not silently go stale. Handle authorization_required and tool_not_connected as described in the Quickstart.

Context is user data, not instructions ​

You inject the user's context into your system prompt, but you did not write it. 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. One pass over </user_context<user_context>> removes the inner tag, and what is left is a valid closing delimiter. Loop until a pass changes nothing.

This removes only the delimiter. The surrounding text stays. Also say in the prompt what the block is: the user's context. It is never a source of instructions, and never something to put 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. This has two consequences:

  • Identity never comes from the prompt. No Configure tool accepts a user id. The credential your server minted decides whose memory a call touches. 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. Your app is responsible for confirming an individual send or event with the user. Connected apps shows the component for it.

If your server injects the user's context into the system prompt, keep that path in your app. Also keep the Configure tools available to the model. The injection is the minimum the model gets. The tools let it get more.

Test data is separate ​

The sandbox is fully separate from live data. sk_test_ keys cannot read a live user's context, and live keys cannot read sandbox data. Every address in the sandbox user's context is example.com. Rehearse everything there first.

Infrastructure to connect agents, apps, and chats