Skip to content

Profile Reference

Create a profile object after a user is linked or identified. configure is a constructed client; see Configure Reference for construction and identity resolution.

ts
const linkedProfile = configure.profile({ token });
const appLocalProfile = configure.profile({ externalId: "customer-123" });

Use token for linked, portable Configure users. Use externalId for unlinked app-local profiles; it is your stable user identifier and the SDK sends it as X-User-Id. Linked profile handles do not need a separate user ID.

Existing agent flow

ts
const profile = configure.profile({ token });
const tools = [
  ...yourTools,
  ...profile.tools(),
];
const executeTool = (toolCall) =>
  toolCall.name.startsWith("configure_")
    ? profile.executeTool(toolCall)
    : executeYourTool(toolCall);

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

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

The browser token stays on the backend. The model receives Configure tool definitions and calls configure_profile_read, configure_profile_search, configure_profile_remember, and configure_profile_forget through your tool router during the turn. Connector, action, utility, and advanced tools are available only when you enable them for that profile runtime.

profile.read(options?)

Reads the current user profile.

ts
const data = await profile.read({
  sections: ["identity", "preferences", "summary"],
});

const approvedContext = data.profile.format({ guidelines: false });

Use host-side reads for UI, debugging, smoke tests, or an app-owned approved context slot. Choose sections when the app knows the packet it wants, or omit sections when it genuinely needs the broad approved overview. Valid sections values: identity, preferences, integrations, imports, agents, summary. The imports section is for user-directed ChatGPT/Claude/etc. memory imports; the integrations section is for connected tools.

box opens one box from the profile read's table of contents. Pass a category ("work"), a source ("agents/parry"), or a shared project tag ("projects/<slug>"). page continues a long box; pages start at 1. since (a date or ISO timestamp) turns a box open into a delta view: only newer notes return, and the result's latest is the cursor to pass back next time. detail: "full" returns whole notes; compact opens cut long notes and mark them truncated: true. page, since, and detail are only meaningful with box, and since composes with detail.

read() returns a ProfileReadResult: { profile, portable, filtered, hiddenSources? }. data.profile.format() renders the returned packet into readable text, but Configure does not own or mutate your system prompt. format() accepts ProfileFormatOptions; guidelines defaults to true, so pass guidelines: false to omit the CONFIGURE_GUIDELINES block. Keep profile.tools() available so the model can call configure_profile_read or configure_profile_search for model-driven overview, concrete memory retrieval, source-specific views, or exact attribution.

profile.search(options?)

Searches or lists permitted attributed profile data. Omit query or pass query: "*" to list bounded permitted results. A bare string is a shorthand: search("travel") equals search({ query: "travel" }).

ts
const results = await profile.search({
  query: "travel preferences",
  source: "your-agent",
  from: "2026-05-01",
  to: "2026-05-31",
  limit: 5,
});

const full = await profile.search({
  query: "travel preferences",
  source: "your-agent",
  detail: "full",
});

const listed = await profile.search({ limit: 50 });

const chatgptImport = await profile.search({
  query: "*",
  source: "imports/chatgpt",
  limit: 50,
});

Omit source to search across permitted attributed profile data for the user. Pass an explicit source handle to filter to that source. Accepted handles: a bare agent name such as tempo, agents/<name> for an agent, and imports/<provider> or import:<provider> for an import. The read digest names sources with the agents/<name> and imports/<provider> forms, so those handles work as filters directly. Bare names that collide with an import provider (chatgpt, claude, gemini, grok, other) are ambiguous: they match both the agent shelf and the import shelf. Use imports/chatgpt or import:chatgpt for import-only results, or agents/chatgpt for agent-only results. source is a filter, not path authority.

box filters to one box id from the profile read's table of contents. It composes with query and source. Shared projects/<slug> boxes work from SDK apps too.

Use search when the user asks for a concrete memory, a source-specific view, or a task where the overview may not be enough. For example: "what did I say about Japan travel?", "what does ChatGPT remember about me?", or "write a briefing personalized to my travel preferences."

detail accepts "compact" or "full"; the default is "compact". Compact results include id, source, text or snippet, type, and when knowable written_by, created_at, event_at, and score. Compact results do not include raw CFS paths or provenance by default. Pass detail: "full" to include safe inspectable metadata such as path, updated_at, markers, and provenance.

search() returns a ProfileSearchResult: { results, filtered, hiddenSources?, visibleSources?, sourceCounts?, diagnostic?, truncated? }. See Types.

profile.remember(fact, options?)

Stores one explicit memory string as a typed CFS memory entry under /agents/{resolved-agent}/memories/{YYYY-MM-DD}/{mem_id}.json.

ts
await profile.remember("User prefers direct, concise status updates.");

// File under a box, including the shared cross-agent project tag:
await profile.remember("[handoff] Billing wired; next: idempotency keys.", {
  box: "projects/billing-v2",
});

remember() accepts bounded memory text, not raw chat message arrays. Use commit() for conversation packets. The optional box files the memory under a namespace shelf; projects/<slug> boxes form the shared cross-agent project view.

remember() returns a RememberResponse: { saved, app, fact, memory? }.

profile.forget(id, options?)

Deletes one of this agent's own memories by id. Own-namespace only: an agent can delete only memories it wrote itself, so a foreign id returns a clean deleted: false, never another agent's data. This mirrors the MCP configure_profile_forget semantics.

ts
await profile.forget("mem_1a2b...", {
  date: "2026-07-01",
  reason: "user_request",
});

options accepts date, a hint for locating the memory, and reason, which is "correction" (the default plain delete) or "user_request". reason: "user_request" is the hard guarantee path: it also suppresses the content hash for this user, so the same content cannot be re-saved or re-imported from any source. Returns a ForgetResponse: { deleted, id, path?, source?, suppressed?, suppressed_copies?, message? }.

profile.import(text, options?)

Bulk-saves a chunk of context (a transcript, long note, or multi-topic dump). Configure distills it into one note and files it into a namespace box; use it instead of many remember() calls when there is too much for one fact.

ts
await profile.import(longPastedTranscript, { box: "projects/billing-v2" });

Omit box to let Configure choose from the content. Returns an ImportResponse: { saved, box, distilled?, memory?, suppressed?, message? }; suppressed: true means the content matched a standing user_request suppression and was not saved.

profile.connect(options?)

Mints a Configure-hosted connect link, the SDK parity of the MCP configure_connect tool. Configure signs and hosts every link, so never build one yourself. Include the returned connect_url (or import_url) verbatim in your reply, and do not retry the underlying action until the user acts on it.

ts
// Sign-in link for a user who has not linked yet.
const signin = await profile.connect();

// Connect or manage a specific app.
const app = await profile.connect({ app: "gmail" });

// Ask for one capability (a scope upgrade), with a reason to show the user.
const permission = await profile.connect({
  capability: "gmail:send",
  purpose: "so it can send the follow-ups you approve",
});

With no options it mints a sign-in link. app targets a connector (gmail, calendar, drive, notion, sheets) or import to open the memory-import page. capability (gmail:read, gmail:send, calendar:read, calendar:write) asks for a single scope upgrade. Returns a ConnectResponse whose status is one of authorization_required (not signed in), connect_app (signed in, managing an app), permission_needed (a capability is missing), or import_ready. Each carries connect_url (or import_url for import_ready), an expires_at, and agent-facing instructions.

profile.commit(input)

Commits a bounded conversation packet after a model turn. After a read-backed turn, bounded messages or toolResults clear the read obligation; memories are optional durable user facts, preferences, or intentions.

ts
await profile.commit({
  messages: [
    { role: "user", content: "I prefer aisle seats." },
    { role: "assistant", content: "I'll remember that for future trips." },
  ],
  response,
  toolResults: [
    { toolName: "configure_gmail_search", content: { matches: 2 } },
  ],
  memories: ["User prefers aisle seats."],
  sync: false,
});

commit() accepts messages, response, toolResults, memories, and sync. It does not accept metadata or internal read-obligation/source-context fields. The SDK bounds the packet before sending it to the backend. Use messages/toolResults for turn evidence; use memories only for explicit durable memory candidates, not assistant provenance. sync is forwarded to the backend commit request.

The SDK enforces these bounds and throws INVALID_INPUT outside them: at most 20 messages, 16,000 characters per message, 20 memories of at most 1,000 characters each, and 50,000 characters total. response is appended as a trailing assistant message: string responses pass through as-is, and non-string responses are serialized with JSON.stringify. Either way it counts against the message bounds. An empty packet throws.

commit() returns a ProfileCommitResult with status: "processing" | "completed", plus facts_written, user_summary (the updated user summary after commit), memories_written, obligations_committed, and rejected_memories.

profile.tools(options?)

Returns model tool definitions for the current profile object.

ts
const defaultTools = profile.tools();
const expandedTools = profile.tools({
  connectors: ["gmail", "calendar"],
  actions: ["email.send", "calendar.create_event"],
  advanced: {
    commit: true,
    files: false,
    utilitySearch: false,
  },
});

The default list contains configure_profile_read, configure_profile_search, configure_profile_remember, and configure_profile_forget.

Connector tools are added through connectors, action tools through actions, and advanced adapter/runtime tools through advanced. Utility web tools are part of advanced; hosted UI helpers are separate from profile.tools(). Tool visibility means app capability, not user authorization. Hosted sign-in, reconnect, permissions, and approval flows establish the user state; profile.executeTool() still fails closed when the user has not linked, connected, consented, permitted, scoped, or approved the capability.

ts
const tools = profile.tools({
  connectors: ["gmail"],
  actions: ["email.send"],
});

When an enabled connector or action cannot run, catch the structured Configure failure and send the user through the hosted connect, reconnect, permissions, or approval surface. Do not teach the model to construct Configure URLs or permission logic.

profile.executeTool(toolCall)

Executes a tool call from the model and returns Promise<unknown>.

ts
const result = await profile.executeTool(toolCall);

toolCall is a ConfigureToolCall. It accepts the common model SDK shapes: { name, arguments }, Anthropic's { name, input }, or OpenAI's { function: { name, arguments } }. arguments can be an object or a JSON string.

ts
type ConfigureToolCall = {
  name?: string;
  arguments?: Record<string, unknown> | string;
  input?: Record<string, unknown>;
  function?: { name?: string; arguments?: Record<string, unknown> | string };
};

The profile object records which connector, action, and advanced tools were enabled by profile.tools(). executeTool() rejects tools that are unknown or not enabled for this object.

Errors

All profile methods throw ConfigureError with a machine-readable code on failure, not just executeTool(). Catch it and branch on error.code. See Errors.

Personalization infrastructure for agents