Skip to main content

Configure SDK for Python

PyPI version

Official Python SDK for Configure — persistent user memory and identity for AI agents.

PR 3.5 parity note: the Python SDK still exposes the legacy /v1/memory/* surface and has not been fully updated for typed memory entry search/detail results. Use the TypeScript SDK or HTTP API for the PR 3.5 typed memory entry contract until Python parity is completed.

Installation

pip install configure-ai

Credentials and the OAuth callback

Credentials come from npx configure setup --users, which opens Configure developer auth once and writes all five values to .env: CONFIGURE_API_KEY, CONFIGURE_PUBLISHABLE_KEY, CONFIGURE_AGENT, CONFIGURE_OAUTH_CLIENT_ID, and CONFIGURE_OAUTH_CLIENT_SECRET.

Everything after that has a Python command:

python -m configure_ai verify                       # real sign-in, token exchange, and one live profile read
python -m configure_ai verify --offline              # no browser: credentials, key, and exact callback registration
python -m configure_ai add-callback --framework fastapi   # or flask, django
python -m configure_ai add-origin https://yourapp.com/auth/configure/callback

verify fails loudly on the mistakes that otherwise surface as an opaque OAuth error mid-integration: a callback that differs from the registration by a port or a trailing slash, a client secret that was reissued out from under a deploy, a publishable key pasted into CONFIGURE_API_KEY. It exits nonzero on any failure, so CI can gate on it.

add-callback writes the callback route, the client_secret_basic code exchange, and the sign-in button snippet for your framework, keeping the secret server-side. The generated browser page recovers the PKCE verifier when state is missing and finishes through Configure.completeSso() in a popup instead of navigating. It never overwrites an existing file unless you pass --force.

add-origin registers a deployed callback on the client in your .env. It opens the dashboard to confirm the exact client and callback, because an sk_ key cannot change an OAuth client, and prints the resulting callbacks. Registration is additive, so one CONFIGURE_OAUTH_CLIENT_ID covers local and production. The dashboard's Sign-in (SSO) page does the same thing by hand.

Quick Start

from configure_ai import ConfigureClient

# Initialize the client
client = ConfigureClient("sk_your_api_key")

# Authenticate user via OTP
client.auth.send_otp("+14155551234")
result = client.auth.verify_otp("+14155551234", "123456")
token = result.token
user_id = result.user_id

# Get user's profile
profile = client.profile.get(token, user_id)
print(f"User: {profile.get('user', {}).get('name', 'Unknown')}")

# Save a memory
client.profile.remember(token, user_id, "User's favorite color is blue")

# Close the client when done
client.close()

Using Context Manager

from configure_ai import ConfigureClient

with ConfigureClient("sk_your_api_key") as client:
    client.auth.send_otp("+14155551234")
    result = client.auth.verify_otp("+14155551234", "123456")
    profile = client.profile.get(result.token, result.user_id)

Async Usage

import asyncio
from configure_ai import AsyncConfigureClient

async def main():
    async with AsyncConfigureClient("sk_your_api_key") as client:
        await client.auth.send_otp("+14155551234")
        result = await client.auth.verify_otp("+14155551234", "123456")

        profile = await client.profile.get(result.token, result.user_id)
        await client.profile.remember(result.token, result.user_id, "User is vegetarian")

asyncio.run(main())

API-Only Unlinked Profiles

If your app already has stable user IDs, you can read and update profiles without hosted auth in the hot path. Pass user_id when constructing the server-side client with your sk_... key; the SDK sends it as X-User-Id.

from configure_ai import ConfigureClient

client = ConfigureClient(
    "sk_your_api_key",
    user_id="your-internal-user-id",
)

profile = client.profile.get()
client.profile.remember(fact="Prefers concise answers")
client.profile.ingest(
    text="Known CRM or onboarding profile text",
    sync=True,
)

This creates an unlinked developer-scoped profile. Other developers' agents cannot read it, and connected tools require the user to link later with hosted auth using the same external ID.

Message-Agent Line Registry

Message agents should register their current provider-owned return line before sending hosted sign-in.me links that include that phone.

from configure_ai import ConfigureClient

client = ConfigureClient("sk_your_api_key", agent="your-agent")
agent_phone = sms_provider.current_phone()

line = client.auth.register_message_line(
    phone=agent_phone,
    channel="sms",
    label="Primary SMS line",
)

lines = client.auth.list_message_lines()
client.auth.revoke_message_line(phone=agent_phone, channel="sms")

Configure stores only a phone hash and last four digits. SDK results never include the raw phone number.

Tool Connections

Connect user accounts to access their data. Tool APIs require an agent-scoped token from hosted auth or trusted headless auth; unlinked user_id profiles can use profile APIs but cannot access connected tools until linked.

# List available tools
tools = client.tools.list(token)
for tool in tools.tools:
    print(f"{tool.name}: {'Connected' if tool.connected else 'Not connected'}")

# Connect Gmail
result = client.tools.connect(token, "gmail", "https://myapp.com/callback")
print(f"Redirect user to: {result.auth_url}")

# After OAuth callback, confirm the connection
confirmation = client.tools.confirm(token, "gmail", result.connection_request_id)

# Search user's emails
emails = client.tools.search_emails(token, user_id, "from:boss@company.com")
for email in emails.emails:
    print(f"- {email.subject}")

# Search every permitted Gmail and Outlook account
emails = client.tools.search_hosted_emails(token, user_id, "shipping update")
if emails.partial:
    print("Some accounts could not be searched")

# Get calendar events
events = client.tools.get_calendar(token, user_id, "week")
for event in events.events:
    print(f"- {event.summary} at {event.start}")

Memory Operations

# Get the full profile
profile = client.profile.get(token, user_id)

# Get a specific path
user_data = client.profile.get(token, user_id, path="user")

# Get agent-specific data
app_data = client.profile.get(token, user_id, sections=["agents"])

# Save a memory
client.profile.remember(token, user_id, "User's preferred language is Spanish")

# Ingest a message for memory extraction
from configure_ai import ConversationMessage

result = client.profile.ingest(
    token,
    user_id,
    ConversationMessage(role="user", content="I always prefer aisle seats on flights"),
    "Travel preferences, dietary restrictions"
)

if result.relevant:
    print(f"Memories extracted: {result.memories_written}")

Profile Operations

Structured read/write access to profile data.

# Agent's own persistent storage
client.self.write("/soul.md", "I am TravelBot...")
soul = client.self.read("/soul.md")
listing = client.self.ls("/")
results = client.self.search("travel preferences")

# User's profile data (token-authenticated or constructor user_id)
summary = client.profile.read(token, user_id, "/summary.md")
client.profile.write(token, user_id, "/agents/travelbot/notes.md", "User prefers budget airlines")

# Peer agent profiles (read-only)
peer_soul = client.peer("wealthbot").read("/soul.md")

API Reference

ConfigureClient / AsyncConfigureClient

Main entry point for the SDK.

ConfigureClient(
    api_key: str,
    base_url: str = "https://api.configure.dev",
    timeout: float = 30.0,
    agent: str | None = None,
    user_id: str | None = None
)

Modules

  • client.auth - Authentication (OTP flow)
  • client.profile - Profile operations (get, remember, ingest, read, write, ls, search, rm)
  • client.tools - Tool connections, search, and sync
  • client.self - Agent persistent storage
  • client.peer(name) - Peer agent data (read-only)

Auth Module

client.auth.send_otp(phone: str) -> OtpStartResponse
client.auth.verify_otp(phone: str, code: str) -> OtpVerifyResponse

Profile Module

client.profile.get(token, user_id, path=None) -> UserProfileResponse  # .format() on response
client.profile.get_memories(token, user_id=None) -> MemoriesResponse
client.profile.remember(token, user_id, fact) -> RememberResponse
client.profile.ingest(token, user_id, messages, sync=True) -> IngestResponse
client.profile.read(token, user_id, path) -> dict | None
client.profile.write(token, user_id, path, content) -> dict
client.profile.ls(token, user_id, path="/") -> dict
client.profile.search(token, user_id, query) -> dict
client.profile.rm(token, user_id, path) -> dict

Tools Module

client.tools.list(token) -> ListToolsResponse
client.tools.connect(token, tool, callback_url=None) -> ConnectToolResponse
client.tools.confirm(token, tool, connection_request_id) -> ConfirmToolResponse
client.tools.sync(token, tool) -> SyncToolResponse
client.tools.disconnect(token, tool) -> None
client.tools.disconnect_all(token) -> None
client.tools.sync_all(token, user_id, tools=None) -> dict
client.tools.search_emails(token, user_id, query, max_results=10) -> SearchEmailsResponse
client.tools.get_calendar(token, user_id, range="week") -> SearchCalendarResponse
client.tools.search_files(token, user_id, query, max_results=10) -> SearchFilesResponse
client.tools.search_notes(token, user_id, query, max_results=10) -> SearchNotesResponse

Error Handling

All SDK methods raise ConfigureError with a typed code property and structured metadata:

from configure_ai import ConfigureError, classify_error

try:
    profile = client.profile.get(token, user_id)
except ConfigureError as e:
    if e.code == "AUTH_REQUIRED":
        # Token expired or invalid — re-authenticate
        # e.suggested_action == "reauthenticate"
        pass
    elif e.code == "RATE_LIMITED":
        # Too many requests — back off and retry
        # e.retryable == True, e.retry_after — seconds to wait
        pass
    elif e.code == "NETWORK_ERROR":
        # Connection failed — check connectivity
        # e.retryable == True
        pass
    else:
        print(f"[{e.code}] {e}")

Errors include structured properties: e.type, e.param, e.retryable, e.suggested_action, e.doc_url, e.retry_after, e.request_id. Use e.retryable to determine if a retry is safe. Use classify_error(error) in agent except blocks to classify any error into a ConfigureError with a friendly message. See full error docs.

Code HTTP Status Meaning
API_KEY_MISSING No API key provided to constructor
AUTH_REQUIRED 401, 403 Invalid or expired token
INVALID_INPUT 400 Bad input (empty fields, path traversal)
TOOL_NOT_CONNECTED 400 Tool action on a disconnected tool
ACCESS_DENIED 403 Not authorized for this resource
TOOL_ERROR varies Tool operation failed (provider error)
PAYMENT_REQUIRED 402 Billing/quota limit reached
NOT_FOUND 404 Resource does not exist
RATE_LIMITED 429 Too many requests
SERVER_ERROR 500+ Server-side error
NETWORK_ERROR Network/connection failure
TIMEOUT Request timed out

Requirements

  • Python 3.8+
  • httpx >= 0.24.0

License

Proprietary. All rights reserved. See configure.dev for licensing information.

Links

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

configure_ai-0.6.0.tar.gz (70.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

configure_ai-0.6.0-py3-none-any.whl (58.1 kB view details)

Uploaded Python 3

File details

Details for the file configure_ai-0.6.0.tar.gz.

File metadata

  • Download URL: configure_ai-0.6.0.tar.gz
  • Upload date:
  • Size: 70.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.12.8

File hashes

Hashes for configure_ai-0.6.0.tar.gz
Algorithm Hash digest
SHA256 b168c1b99a322f23a3fd081d253aa8c7e26b1ddc3639c152fd10dd1b28ad9044
MD5 845ced9ea21169a7126d5ec662d524fb
BLAKE2b-256 d323f75e93b66d70ac93d8c82c5c8238554a27bb1b99189e8648c960918dfcd8

See more details on using hashes here.

File details

Details for the file configure_ai-0.6.0-py3-none-any.whl.

File metadata

  • Download URL: configure_ai-0.6.0-py3-none-any.whl
  • Upload date:
  • Size: 58.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.12.8

File hashes

Hashes for configure_ai-0.6.0-py3-none-any.whl
Algorithm Hash digest
SHA256 e7a8228bdb4bf43c718987524edc8a18a1c9828a994f01b87d98b34f2a8f46f3
MD5 62efe3f3a8454343948116f841309278
BLAKE2b-256 18612b63e64fbe36b998890adc5ffddc05a8f6b13f6f404f86c57be617854951

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page