# Authentication Source: https://docs.getprofile.org/api-reference/authentication Authenticate with the GetProfile API ## API Key (Optional) GetProfile uses a simple API key authentication system. For self-hosted deployments, you can optionally protect your proxy with an API key. ## Configuration Set the `GETPROFILE_API_KEY` environment variable: ```bash theme={null} GETPROFILE_API_KEY=your-secret-key-here ``` If not set, the proxy will accept all requests (useful for local development). ## Headers ### Required Headers | Header | Description | | --------------- | ----------------------------------------------------------- | | `Authorization` | Bearer token (only required if `GETPROFILE_API_KEY` is set) | ### Optional Headers | Header | Description | | --------------------- | ---------------------------------- | | `X-GetProfile-Id` | Your app's user identifier | | `X-Upstream-Key` | API key for upstream LLM provider | | `X-GetProfile-Traits` | Per-request trait overrides (JSON) | ## Authentication Methods ### Bearer Token (if API key is configured) ```bash theme={null} curl http://localhost:3100/v1/chat/completions \ -H "Authorization: Bearer your-secret-key-here" \ -H "Content-Type: application/json" \ -d '{"model": "gpt-5", "messages": [...]}' ``` ### No Authentication (local development) If `GETPROFILE_API_KEY` is not set, you can make requests without authentication: ```bash theme={null} curl http://localhost:3100/v1/chat/completions \ -H "Content-Type: application/json" \ -d '{"model": "gpt-5", "messages": [...]}' ``` ### SDK Usage ```typescript TypeScript theme={null} import OpenAI from 'openai'; const client = new OpenAI({ apiKey: process.env.GETPROFILE_API_KEY || 'not-needed-for-local', baseURL: 'http://localhost:3100/v1', defaultHeaders: { 'X-GetProfile-Id': 'user-123', 'X-Upstream-Key': 'sk-your-openai-key', }, }); ``` ```python Python theme={null} from openai import OpenAI import os client = OpenAI( api_key=os.getenv("GETPROFILE_API_KEY", "not-needed-for-local"), base_url="http://localhost:3100/v1", default_headers={ "X-GetProfile-Id": "user-123", "X-Upstream-Key": "sk-your-openai-key", }, ) ``` ## User Identification The `X-GetProfile-Id` header identifies which user the request is for. This can be: * Your app's user ID * A session ID * Any stable identifier for the user Alternatively, you can use: * `user` field in request body (OpenAI standard) * `metadata.profile_id` in request body ```typescript theme={null} // Option 1: Header (recommended) headers: { 'X-GetProfile-Id': 'user-123' } // Option 2: Body field body: { user: 'user-123', ... } // Option 3: Metadata body: { metadata: { profile_id: 'user-123' }, ... } ``` ## Upstream Authentication The `X-Upstream-Key` header provides the API key for the upstream LLM provider: ```bash theme={null} curl http://localhost:3100/v1/chat/completions \ -H "Authorization: Bearer ${GETPROFILE_API_KEY:-not-needed}" \ -H "X-Upstream-Key: sk-your-openai-key" \ -H "X-GetProfile-Id: user-123" \ ... ``` If you configure a default upstream key in your GetProfile settings, `X-Upstream-Key` is optional. ## Error Responses | Status | Error | Description | | ------ | ----------------- | ---------------------------------------------------- | | 401 | `missing_api_key` | No Authorization header (when API key is configured) | | 401 | `invalid_api_key` | API key does not match `GETPROFILE_API_KEY` | | 400 | `missing_user_id` | No user identifier provided | # Create Memory Source: https://docs.getprofile.org/api-reference/memories/create POST /api/profiles/{id}/memories Manually add a memory to a profile ## Overview Manually creates a new memory for a user profile. Use this to add context that wasn't captured during conversations. ## Path Parameters Profile ID ## Request Body The memory content Memory type: `fact`, `preference`, `event`, or `context` Importance score (0.0-1.0). Default: 0.5 ## Response ```json theme={null} { "memory": { "id": "mem-125", "content": "Customer has enterprise support contract", "type": "fact", "importance": 0.9, "decayFactor": 1.0, "createdAt": "2024-01-15T00:00:00Z", "lastAccessedAt": null } } ``` ## Example ```bash theme={null} curl -X POST https://api.yourserver.com/api/profiles/user-123/memories \ -H "Authorization: Bearer gp_your_key" \ -H "Content-Type: application/json" \ -d '{ "content": "Customer has enterprise support contract", "type": "fact", "importance": 0.9 }' ``` # Delete Memory Source: https://docs.getprofile.org/api-reference/memories/delete DELETE /api/profiles/{id}/memories/{memoryId} Delete a memory from a profile ## Overview Removes a specific memory from a user profile. ## Path Parameters Profile ID Memory ID to delete ## Response ```json theme={null} { "success": true } ``` ## Example ```bash theme={null} curl -X DELETE https://api.yourserver.com/api/profiles/user-123/memories/mem-125 \ -H "Authorization: Bearer gp_your_key" ``` # List Memories Source: https://docs.getprofile.org/api-reference/memories/list GET /api/profiles/{id}/memories List memories for a profile ## Overview Returns memories associated with a user profile, sorted by importance and recency. ## Path Parameters Profile ID ## Query Parameters Filter by memory type: `fact`, `preference`, `event`, `context` Maximum number of memories to return ## Response ```json theme={null} { "memories": [ { "id": "mem-123", "content": "Working on microservices migration at work", "type": "event", "importance": 0.8, "decayFactor": 0.95, "createdAt": "2024-01-14T00:00:00Z", "lastAccessedAt": "2024-01-15T00:00:00Z" }, { "id": "mem-124", "content": "Prefers async/await patterns over callbacks", "type": "preference", "importance": 0.6, "decayFactor": 1.0, "createdAt": "2024-01-10T00:00:00Z", "lastAccessedAt": null } ] } ``` ## Example ```bash theme={null} # Get all memories curl https://api.yourserver.com/api/profiles/user-123/memories \ -H "Authorization: Bearer gp_your_key" # Get only facts curl "https://api.yourserver.com/api/profiles/user-123/memories?type=fact&limit=10" \ -H "Authorization: Bearer gp_your_key" ``` # API Overview Source: https://docs.getprofile.org/api-reference/overview GetProfile API reference ## Base URLs | Environment | URL | | ----------- | ---------------------------- | | Self-hosted | `http://localhost:3100` | | Production | `https://api.yourserver.com` | ## API Groups OpenAI-compatible LLM proxy endpoints Manage user profiles View and update user traits Access user memories ## Endpoint Summary ### Proxy Endpoints (OpenAI-compatible) | Method | Endpoint | Description | | ------ | ---------------------- | --------------------------- | | POST | `/v1/chat/completions` | Chat completion with memory | | GET | `/v1/models` | List available models | ### Profile Endpoints | Method | Endpoint | Description | | ------ | -------------------------- | --------------------------------------- | | GET | `/api/profiles` | List all profiles | | POST | `/api/profiles` | Create or get profile by external ID | | GET | `/api/profiles/:id` | Get profile details | | DELETE | `/api/profiles/:id` | Delete profile | | GET | `/api/profiles/:id/export` | Export profile data | | POST | `/api/profiles/:id/ingest` | Ingest data and extract traits/memories | ### Trait Endpoints | Method | Endpoint | Description | | ------ | ------------------------------- | ------------ | | GET | `/api/profiles/:id/traits` | List traits | | PUT | `/api/profiles/:id/traits/:key` | Update trait | | DELETE | `/api/profiles/:id/traits/:key` | Delete trait | ### Memory Endpoints | Method | Endpoint | Description | | ------ | -------------------------------------- | ------------- | | GET | `/api/profiles/:id/memories` | List memories | | POST | `/api/profiles/:id/memories` | Create memory | | DELETE | `/api/profiles/:id/memories/:memoryId` | Delete memory | ### Utility Endpoints | Method | Endpoint | Description | | ------ | --------- | --------------- | | GET | `/health` | Health check | | GET | `/ready` | Readiness check | # Delete Profile Source: https://docs.getprofile.org/api-reference/profiles/delete DELETE /api/profiles/{id} Delete a user profile and all associated data ## Overview Deletes a user profile and cascade-deletes all associated data: * Traits * Memories * Messages This endpoint is designed for GDPR right-to-erasure compliance. ## Path Parameters Profile ID (internal UUID or external ID) ## Response ```json theme={null} { "success": true, "deleted": { "traits": 8, "memories": 24, "messages": 156 } } ``` ## Example ```bash theme={null} curl -X DELETE https://api.yourserver.com/api/profiles/user-123 \ -H "Authorization: Bearer gp_your_key" ``` This action is irreversible. All profile data will be permanently deleted. # Export Profile Source: https://docs.getprofile.org/api-reference/profiles/export GET /api/profiles/{id}/export Export all profile data ## Overview Exports all data associated with a user profile in a portable JSON format. This endpoint is designed for GDPR data portability compliance. ## Path Parameters Profile ID (internal UUID or external ID) ## Response ```json theme={null} { "profile": { "id": "550e8400-e29b-41d4-a716-446655440000", "externalId": "user-123", "summary": "Alex is an experienced software engineer...", "createdAt": "2024-01-01T00:00:00Z", "updatedAt": "2024-01-15T10:30:00Z" }, "traits": [ { "key": "name", "value": "Alex", "confidence": 0.95, "source": "extracted", "createdAt": "2024-01-01T00:00:00Z" } ], "memories": [ { "id": "mem-123", "content": "Working on microservices migration", "type": "event", "importance": 0.8, "createdAt": "2024-01-14T00:00:00Z" } ], "messages": [ { "id": "msg-123", "role": "user", "content": "Hello!", "createdAt": "2024-01-14T00:00:00Z" } ], "exportedAt": "2024-01-15T12:00:00Z" } ``` ## Example ```bash theme={null} curl https://api.yourserver.com/api/profiles/user-123/export \ -H "Authorization: Bearer gp_your_key" \ -o profile-export.json ``` # Get Profile Source: https://docs.getprofile.org/api-reference/profiles/get GET /api/profiles/{id} Get a specific user profile with traits ## Overview Returns a user profile with all associated traits and recent memories. ## Path Parameters Profile ID (internal UUID or external ID) ## Response ```json theme={null} { "profile": { "id": "550e8400-e29b-41d4-a716-446655440000", "externalId": "user-123", "summary": "Alex is an experienced software engineer who prefers concise, technical explanations.", "summaryVersion": 3, "summaryUpdatedAt": "2024-01-15T10:30:00Z", "createdAt": "2024-01-01T00:00:00Z", "updatedAt": "2024-01-15T10:30:00Z", "traits": [ { "key": "name", "value": "Alex", "confidence": 0.95, "source": "extracted", "createdAt": "2024-01-01T00:00:00Z", "updatedAt": "2024-01-01T00:00:00Z" }, { "key": "expertise_level", "value": "advanced", "confidence": 0.8, "source": "extracted", "createdAt": "2024-01-01T00:00:00Z", "updatedAt": "2024-01-01T00:00:00Z" } ] }, "recentMemories": [ { "id": "mem-123", "content": "Working on microservices migration", "type": "event", "importance": 0.8, "createdAt": "2024-01-14T00:00:00Z" } ] } ``` ## Example ```bash theme={null} curl https://api.yourserver.com/api/profiles/user-123 \ -H "Authorization: Bearer gp_your_key" ``` # Ingest Data Source: https://docs.getprofile.org/api-reference/profiles/ingest POST /api/profiles/{id}/ingest Extract traits and memories from arbitrary text data ## Overview Ingest arbitrary text data and extract user traits and memories synchronously. This endpoint allows you to process historical data, CRM notes, chat logs, emails, or any text containing user information. Unlike the LLM proxy which processes conversations in real-time, this endpoint provides immediate extraction results, making it ideal for batch imports and offline processing. ## Use Cases * **Historical chat log import** - Bulk import past conversations * **CRM integration** - Extract user info from Salesforce/HubSpot notes * **Email thread processing** - Analyze support ticket histories * **Batch migrations** - Import user data from other systems * **Offline processing** - Extract traits without live LLM calls ## Path Parameters Profile ID (internal UUID or external ID) ## Request Body The text data to ingest (max 100KB). Can be any text containing user information. Optional source identifier (e.g., "crm", "chat\_log", "email", "salesforce") Optional metadata object for tracking (e.g., `{ "salesforceId": "abc123" }`) Whether to extract traits from the data Whether to extract memories from the data ## Response The updated profile information Extraction results Array of extracted traits with `key`, `value`, `confidence`, and `action` (create/update) Array of extracted memories Statistics: `traitsCreated`, `traitsUpdated`, `memoriesCreated` The source passed in the request The metadata passed in the request ## Response Example ```json theme={null} { "profile": { "id": "550e8400-e29b-41d4-a716-446655440000", "externalId": "user-123", "summary": "Alex is a senior engineer at Acme Corp...", "createdAt": "2024-01-01T00:00:00Z", "updatedAt": "2024-01-15T10:30:00Z" }, "extracted": { "traits": [ { "key": "name", "value": "Alex", "confidence": 0.9, "action": "create" }, { "key": "expertise_level", "value": "senior", "confidence": 0.85, "action": "update" }, { "key": "interests", "value": ["TypeScript", "AI"], "confidence": 0.8, "action": "update" } ], "memories": [ { "id": "mem-456", "content": "Works at Acme Corp", "type": "fact", "importance": 0.8, "createdAt": "2024-01-15T10:30:00Z" }, { "id": "mem-457", "content": "Prefers technical communication", "type": "preference", "importance": 0.7, "createdAt": "2024-01-15T10:30:00Z" } ], "stats": { "traitsCreated": 1, "traitsUpdated": 2, "memoriesCreated": 2 } }, "source": "crm", "metadata": { "salesforceId": "abc123", "importedBy": "migration-script" } } ``` ## Examples ### Basic Usage ```bash theme={null} curl -X POST https://api.yourserver.com/api/profiles/user-123/ingest \ -H "Authorization: Bearer gp_your_key" \ -H "Content-Type: application/json" \ -d '{ "data": "Alex is a senior engineer at Acme Corp. Prefers TypeScript and technical communication." }' ``` ### CRM Data Import ```bash theme={null} curl -X POST https://api.yourserver.com/api/profiles/user-123/ingest \ -H "Authorization: Bearer gp_your_key" \ -H "Content-Type: application/json" \ -d '{ "data": "Customer Alex Thompson from Acme Corp. Contact preference: email. Technical background, 10+ years experience. Currently evaluating our enterprise plan. Key decision maker for engineering tools.", "source": "crm", "metadata": { "salesforceId": "0035000000abc123", "accountName": "Acme Corp", "importDate": "2024-01-15" } }' ``` ### Chat Log Import ```bash theme={null} curl -X POST https://api.yourserver.com/api/profiles/user-123/ingest \ -H "Authorization: Bearer gp_your_key" \ -H "Content-Type: application/json" \ -d '{ "data": "User: Hi, I am Alex. I work as a senior engineer.\nAssistant: Nice to meet you!\nUser: I prefer concise, technical explanations.\nAssistant: Got it, I will keep responses technical and to the point.", "source": "chat_log", "metadata": { "sessionId": "sess_abc123", "platform": "web" } }' ``` ### Extract Only Traits ```bash theme={null} curl -X POST https://api.yourserver.com/api/profiles/user-123/ingest \ -H "Authorization: Bearer gp_your_key" \ -H "Content-Type: application/json" \ -d '{ "data": "Alex, senior software engineer, prefers TypeScript", "extractTraits": true, "extractMemories": false }' ``` ## Error Responses ```json 400 theme={null} { "error": { "message": "data is required and must be a non-empty string", "type": "invalid_request_error", "code": "invalid_data" } } ``` ```json 400 theme={null} { "error": { "message": "data size exceeds maximum of 102400 bytes", "type": "invalid_request_error", "code": "data_too_large" } } ``` ```json 404 theme={null} { "error": { "message": "Profile not found", "type": "not_found", "code": "profile_not_found" } } ``` ```json 500 theme={null} { "error": { "message": "Trait extraction failed", "type": "internal_error", "code": "extraction_error" } } ``` ## Best Practices 1. **Data Size** - Keep data under 100KB per request. For larger datasets, split into multiple requests. 2. **Source Tracking** - Use the `source` field to track where data originated for audit trails. 3. **Metadata** - Include relevant metadata to link back to source systems (CRM IDs, session IDs, etc.). 4. **Batch Processing** - For bulk imports, process profiles sequentially to avoid rate limits. 5. **Error Handling** - Implement retry logic for transient failures (500 errors). ## Comparison with Live Proxy | Feature | Ingest Endpoint | LLM Proxy | | ------------------- | --------------------- | --------------------------- | | **Use Case** | Batch/historical data | Real-time conversations | | **Processing** | Synchronous | Background (async) | | **Returns Results** | Yes (immediate) | No (extracts in background) | | **Data Format** | Any text | OpenAI chat format | | **Rate Limit** | Separate limit | Standard proxy limit | # List Profiles Source: https://docs.getprofile.org/api-reference/profiles/list GET /api/profiles List all user profiles ## Overview Returns a paginated list of all user profiles. ## Query Parameters Maximum number of profiles to return Number of profiles to skip Search by external ID ## Response ```json theme={null} { "profiles": [ { "id": "550e8400-e29b-41d4-a716-446655440000", "externalId": "user-123", "summary": "Alex is an experienced software engineer...", "summaryVersion": 3, "summaryUpdatedAt": "2024-01-15T10:30:00Z", "createdAt": "2024-01-01T00:00:00Z", "updatedAt": "2024-01-15T10:30:00Z" } ], "total": 1 } ``` ## Example ```bash theme={null} curl "https://api.yourserver.com/api/profiles?limit=10&offset=0" \ -H "Authorization: Bearer gp_your_key" ``` # Chat Completions Source: https://docs.getprofile.org/api-reference/proxy/chat-completions POST /v1/chat/completions OpenAI-compatible chat completion endpoint ## Overview The chat completions endpoint is fully compatible with OpenAI's API. GetProfile automatically: 1. Loads the user's profile 2. Injects relevant context into the system message 3. Forwards the request to the upstream provider 4. Extracts traits and memories in the background ## Request Model to use (e.g., `gpt-5`, `gpt-5-mini`) Array of message objects Enable streaming responses User identifier (alternative to `X-GetProfile-Id` header) GetProfile-specific options (stripped before forwarding) Per-request trait schema overrides Skip context injection for this request Skip background extraction for this request ## Response Standard OpenAI chat completion response. ```json theme={null} { "id": "chatcmpl-abc123", "object": "chat.completion", "created": 1704067200, "model": "gpt-5", "choices": [ { "index": 0, "message": { "role": "assistant", "content": "Hello Alex! How can I help you today?" }, "finish_reason": "stop" } ], "usage": { "prompt_tokens": 50, "completion_tokens": 12, "total_tokens": 62 } } ``` ## Examples ### Basic Request ```bash cURL theme={null} curl https://api.yourserver.com/v1/chat/completions \ -H "Authorization: Bearer gp_your_key" \ -H "X-GetProfile-Id: user-123" \ -H "X-Upstream-Key: sk-openai-key" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-5", "messages": [ {"role": "user", "content": "Hello!"} ] }' ``` ```typescript TypeScript theme={null} const response = await client.chat.completions.create({ model: "gpt-5", messages: [{ role: "user", content: "Hello!" }], }); ``` ### Streaming ```bash cURL theme={null} curl https://api.yourserver.com/v1/chat/completions \ -H "Authorization: Bearer gp_your_key" \ -H "X-GetProfile-Id: user-123" \ -H "X-Upstream-Key: sk-openai-key" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-5", "messages": [{"role": "user", "content": "Hello!"}], "stream": true }' ``` ```typescript TypeScript theme={null} const stream = await client.chat.completions.create({ model: "gpt-5", messages: [{ role: "user", content: "Hello!" }], stream: true, }); for await (const chunk of stream) { process.stdout.write(chunk.choices[0]?.delta?.content || ""); } ``` ### Per-Request Traits ```typescript theme={null} const response = await client.chat.completions.create({ model: "gpt-5", messages: [{ role: "user", content: "Help me plan my trip" }], // @ts-ignore - GetProfile extension getprofile: { traits: [ { key: "travel_preferences", valueType: "object", extraction: { enabled: true }, injection: { enabled: true, template: "Travel prefs: {{value}}" }, }, ], }, }); ``` ### Skip Processing ```typescript theme={null} // Skip context injection (raw request) const response = await client.chat.completions.create({ model: "gpt-5", messages: [{ role: "user", content: "Hello!" }], // @ts-ignore getprofile: { skipInjection: true }, }); // Skip background extraction const response = await client.chat.completions.create({ model: "gpt-5", messages: [{ role: "user", content: "Hello!" }], // @ts-ignore getprofile: { skipExtraction: true }, }); ``` # List Models Source: https://docs.getprofile.org/api-reference/proxy/models GET /v1/models List available models from upstream provider ## Overview Lists available models from the configured upstream LLM provider. This endpoint forwards to the upstream provider's models endpoint. ## Request No request body required. ## Response ```json theme={null} { "object": "list", "data": [ { "id": "gpt-5", "object": "model", "created": 1704067200, "owned_by": "openai" }, { "id": "gpt-5-mini", "object": "model", "created": 1704067200, "owned_by": "openai" }, { "id": "gpt-3.5-turbo", "object": "model", "created": 1704067200, "owned_by": "openai" } ] } ``` ## Example ```bash theme={null} curl https://api.yourserver.com/v1/models \ -H "Authorization: Bearer gp_your_key" \ -H "X-Upstream-Key: sk-openai-key" ``` # Delete Trait Source: https://docs.getprofile.org/api-reference/traits/delete DELETE /api/profiles/{id}/traits/{key} Delete a trait from a profile ## Overview Removes a trait from a user profile. ## Path Parameters Profile ID Trait key to delete ## Response ```json theme={null} { "success": true } ``` ## Example ```bash theme={null} curl -X DELETE https://api.yourserver.com/api/profiles/user-123/traits/name \ -H "Authorization: Bearer gp_your_key" ``` # List Traits Source: https://docs.getprofile.org/api-reference/traits/list GET /api/profiles/{id}/traits List all traits for a profile ## Overview Returns all traits associated with a user profile. ## Path Parameters Profile ID ## Response ```json theme={null} { "traits": [ { "key": "name", "value": "Alex", "valueType": "string", "category": "identity", "confidence": 0.95, "source": "extracted", "createdAt": "2024-01-01T00:00:00Z", "updatedAt": "2024-01-01T00:00:00Z" }, { "key": "expertise_level", "value": "advanced", "valueType": "enum", "category": "context", "confidence": 0.8, "source": "extracted", "createdAt": "2024-01-01T00:00:00Z", "updatedAt": "2024-01-05T00:00:00Z" } ] } ``` ## Example ```bash theme={null} curl https://api.yourserver.com/api/profiles/user-123/traits \ -H "Authorization: Bearer gp_your_key" ``` # Update Trait Source: https://docs.getprofile.org/api-reference/traits/update PUT /api/profiles/{id}/traits/{key} Create or update a trait ## Overview Creates a new trait or updates an existing one. Manually set traits have `source: "manual"` and are preserved during automatic extraction. ## Path Parameters Profile ID Trait key (e.g., `name`, `expertise_level`) ## Request Body The trait value (type must match schema) Confidence score (0.0-1.0). Defaults to 1.0 for manual updates. ## Response ```json theme={null} { "trait": { "key": "name", "value": "Alexander", "valueType": "string", "category": "identity", "confidence": 1.0, "source": "manual", "createdAt": "2024-01-01T00:00:00Z", "updatedAt": "2024-01-15T00:00:00Z" } } ``` ## Example ```bash theme={null} curl -X PUT https://api.yourserver.com/api/profiles/user-123/traits/name \ -H "Authorization: Bearer gp_your_key" \ -H "Content-Type: application/json" \ -d '{"value": "Alexander", "confidence": 1.0}' ``` # JavaScript/TypeScript SDK Source: https://docs.getprofile.org/client-libraries/javascript Full-featured client library for GetProfile The official JavaScript/TypeScript SDK provides complete programmatic control over GetProfile, including OpenAI-compatible chat completions plus full profile, trait, and memory management. ## Installation ```bash theme={null} npm install @getprofile/sdk-js # or pnpm add @getprofile/sdk-js # or yarn add @getprofile/sdk-js ``` ## Quick Start ```typescript theme={null} import { GetProfileClient } from "@getprofile/sdk-js"; const client = new GetProfileClient({ apiKey: "gp_your_api_key", baseUrl: "http://localhost:3100", // Your GetProfile instance }); // OpenAI-compatible chat with automatic personalization const completion = await client.chat.completions.create({ model: "gpt-5-mini", messages: [{ role: "user", content: "What should I work on today?" }], user: "user-123", // Automatically injects this user's context }); console.log(completion.choices[0].message.content); ``` ## Chat Completions (OpenAI Compatible) GetProfile's primary feature is OpenAI-compatible chat completions with automatic user context injection. Just pass a `user` parameter and GetProfile handles the rest. ### Non-Streaming ```typescript theme={null} const completion = await client.chat.completions.create({ model: "gpt-5-mini", messages: [ { role: "system", content: "You are a helpful assistant." }, { role: "user", content: "Help me plan my day" }, ], user: "user-123", // Automatically injects user's traits and memories }); console.log(completion.choices[0].message.content); ``` ### Streaming ```typescript theme={null} const stream = await client.chat.completions.create({ model: "gpt-5-mini", messages: [{ role: "user", content: "Write me a personalized workout plan" }], stream: true, user: "user-123", }); for await (const chunk of stream) { const content = chunk.choices[0]?.delta?.content || ""; process.stdout.write(content); } ``` ### Controlling Extraction and Injection You can control what gets extracted and injected on a per-request basis: ```typescript theme={null} // Skip context injection for this request const completion = await client.chat.completions.create({ model: "gpt-5-mini", messages: [{ role: "user", content: "Generic query" }], user: "user-123", getprofile: { skipInjection: true, // Don't inject user context }, }); // Skip extraction for this request const completion = await client.chat.completions.create({ model: "gpt-5-mini", messages: [{ role: "user", content: "Casual chat" }], user: "user-123", getprofile: { skipExtraction: true, // Don't learn from this conversation }, }); // Custom trait schema for domain-specific extraction const completion = await client.chat.completions.create({ model: "gpt-5-mini", messages: [{ role: "user", content: "Plan my trip to Japan" }], user: "user-123", getprofile: { traits: [ { key: "travel_budget", valueType: "enum", extraction: { enabled: true, promptSnippet: "Extract budget preference: low, medium, high", }, injection: { enabled: true, template: "User budget: {{value}}", priority: 8, }, }, ], }, }); ``` ### Supported Parameters All standard OpenAI chat completion parameters are supported: * `model` - Model to use (e.g., 'gpt-5-mini', 'gpt-5', 'gpt-4-turbo') * `messages` - Array of chat messages * `user` - User identifier for automatic profile context injection * `temperature`, `top_p`, `frequency_penalty`, `presence_penalty` * `max_tokens`, `stop` * `stream` - Enable streaming responses * `getprofile` - GetProfile-specific options (skipInjection, skipExtraction, traits) ## Models ### List Available Models ```typescript theme={null} const models = await client.models.list(); console.log(models.data); // Array of available models ``` ## Profiles ### Get or Create Profile ```typescript theme={null} // Creates profile if it doesn't exist const profile = await client.profiles.getOrCreate("your-user-id"); ``` This call hits `POST /api/profiles`, which is idempotent—if the profile already exists the current record is returned. Call it whenever a user signs in to guarantee they have a profile before you store traits or memories. ### Get Profile with Details ```typescript theme={null} const profile = await client.profiles.get(profileId); // Returns: { profile, traits, recentMemories } ``` ### List Profiles ```typescript theme={null} const { profiles, total } = await client.profiles.list({ limit: 20, offset: 0, search: "alex", // Optional search }); ``` ### Delete Profile ```typescript theme={null} // Cascade deletes all traits, memories, and messages const result = await client.profiles.delete(profileId); console.log(`Deleted ${result.deleted.traits} traits`); ``` ### Export Profile Data ```typescript theme={null} // GDPR-compliant data export const exportData = await client.profiles.export(profileId); // Returns: { profile, traits, memories, messages, exportedAt } ``` ### Ingest Data Extract traits and memories from arbitrary text data: ```typescript theme={null} // Ingest CRM notes const result = await client.ingestData( "user-123", "Customer Alex from Acme Corp. Senior engineer with 10 years experience. Prefers email communication.", { source: "crm", metadata: { salesforceId: "abc123" }, } ); console.log(`Created ${result.extracted.stats.traitsCreated} traits`); console.log(`Updated ${result.extracted.stats.traitsUpdated} traits`); console.log(`Created ${result.extracted.stats.memoriesCreated} memories`); // Access extracted data result.extracted.traits.forEach((trait) => { console.log(`${trait.key}: ${trait.value} (${trait.action})`); }); ``` **Common use cases:** ```typescript theme={null} // Import historical chat logs await client.ingestData(profileId, chatLogText, { source: "chat_log", metadata: { sessionId: "sess_123" }, }); // Process email threads await client.ingestData(profileId, emailThread, { source: "email", extractTraits: true, extractMemories: true, }); // Extract only traits (skip memories) await client.ingestData(profileId, text, { extractTraits: true, extractMemories: false, }); ``` ## Traits ### Get All Traits ```typescript theme={null} const traits = await client.traits.list(profileId); ``` ### Update Trait ```typescript theme={null} const trait = await client.traits.update(profileId, "expertise_level", { value: "advanced", confidence: 0.85, }); ``` ### Delete Trait ```typescript theme={null} await client.traits.delete(profileId, "outdated_trait"); ``` ## Memories ### List Memories ```typescript theme={null} const memories = await client.memories.list(profileId, { type: "fact", // Optional: 'fact' | 'preference' | 'event' | 'context' limit: 20, }); ``` ### Add Memory ```typescript theme={null} const memory = await client.memories.create(profileId, { content: "User prefers TypeScript over JavaScript", type: "preference", importance: 0.7, }); ``` ### Delete Memory ```typescript theme={null} await client.memories.delete(profileId, memoryId); ``` ## TypeScript Support The SDK is fully typed. Import types as needed: ```typescript theme={null} import type { Profile, Trait, Memory, TraitSchema, MemoryType, TraitValueType, IngestDataOptions, IngestResult, } from "@getprofile/sdk-js"; ``` ## Error Handling ```typescript theme={null} import { GetProfileError } from "@getprofile/sdk-js"; try { await client.profiles.get("non-existent-id"); } catch (error) { if (error instanceof GetProfileError) { console.error(`API Error: ${error.message}`); console.error(`Status: ${error.status}`); } } ``` ## Configuration Options ```typescript theme={null} const client = new GetProfileClient({ apiKey: "gp_...", baseUrl: "http://localhost:3100", // Optional settings timeout: 30000, // Request timeout in ms retries: 3, // Number of retries on failure }); ``` ## Using with Core Package For more control, you can use the core package directly: ```typescript theme={null} import { ProfileManager, MemoryEngine, TraitEngine } from "@getprofile/core"; import { db } from "@getprofile/db"; const profileManager = new ProfileManager({ memory: { /* config */ }, traits: { /* config */ }, }); // Full access to engine internals const context = await profileManager.buildContext(profileId, query); ``` ## Next Steps Complete API documentation Configure what to extract # Memories Source: https://docs.getprofile.org/concepts/memories Extracted facts and context from conversations ## What are Memories? Memories are discrete pieces of information extracted from user conversations. They complement traits by storing: * Specific facts that don't fit trait schemas * Events and experiences mentioned * Contextual information for future conversations ## Memory Structure ```typescript theme={null} interface Memory { id: string; profileId: string; content: string; // The extracted information type: 'fact' | 'preference' | 'event' | 'context'; importance: number; // 0.0 - 1.0 decayFactor: number; // Decreases over time sourceMessageIds: string[]; createdAt: Date; lastAccessedAt: Date | null; } ``` ## Memory Types Concrete information: "Works at Acme Corp as a senior engineer" Likes and dislikes: "Prefers dark mode IDEs" Things that happened: "Started a new project last week" Situational info: "Currently debugging a production issue" ## Importance Scoring Memories are scored by likely future relevance: | Score | Description | Examples | | ------- | ------------------ | ------------------------------- | | 0.8+ | Core identity/role | Job title, primary tech stack | | 0.6-0.8 | Ongoing projects | Current goals, active work | | 0.4-0.6 | Preferences | Tool preferences, working style | | \< 0.4 | Ephemeral context | Today's specific task | ## Memory Decay Over time, memories become less relevant. The `decayFactor` (starting at 1.0) decreases: * Unused memories decay faster * Accessed memories are "refreshed" * Low importance memories decay faster This ensures recent and frequently-relevant information takes priority. ## Memory Retrieval When building context for a request, GetProfile retrieves memories based on: 1. **Recency**: More recent memories score higher 2. **Importance**: Higher importance memories are prioritized 3. **Relevance**: (Future) Semantic similarity to the current query 4. **Access patterns**: Frequently accessed memories are boosted ## Example: Memory Extraction Given this conversation: ``` User: I'm working on migrating our monolith to microservices at work. We're using Kubernetes and having some issues with the service mesh. By the way, I'll be on vacation next week. ``` GetProfile extracts: ```json theme={null} [ { "content": "Working on migrating a monolith to microservices", "type": "event", "importance": 0.8 }, { "content": "Uses Kubernetes at work", "type": "fact", "importance": 0.7 }, { "content": "Experiencing issues with service mesh", "type": "context", "importance": 0.6 }, { "content": "Will be on vacation next week", "type": "event", "importance": 0.4 } ] ``` ## Deduplication GetProfile automatically handles duplicate memories: * Identical content is merged * Similar memories may be consolidated * Source messages are aggregated # Profiles Source: https://docs.getprofile.org/concepts/profiles Understanding user profiles in GetProfile ## What is a Profile? A profile is GetProfile's representation of a user. It stores: * **Identity**: External ID (your app's user identifier) * **Traits**: Structured attributes extracted from conversations * **Summary**: AI-generated natural language description * **Metadata**: Creation time, last update, summary version ## Profile Structure ```typescript theme={null} interface Profile { id: string; // Internal UUID externalId: string; // Your app's user ID summary: string | null; summaryVersion: number; summaryUpdatedAt: Date | null; createdAt: Date; updatedAt: Date; } ``` ## External ID vs Profile ID It's important to understand the difference between these two identifiers. | Field | Description | Example | | ------------ | -------------------------- | -------------------------------------- | | `externalId` | Your app's user identifier | `user_abc123` | | `id` | GetProfile's internal UUID | `550e8400-e29b-41d4-a716-446655440000` | When making requests to the proxy, you always use your **external ID** via the `X-GetProfile-Id` header. GetProfile maps this to the internal profile ID automatically. ## Profile Creation Profiles are created automatically when a user makes their first request: ```typescript theme={null} // First request with a new user ID const response = await client.chat.completions.create( { model: "gpt-5", messages: [{ role: "user", content: "Hello!" }], }, { headers: { "X-GetProfile-Id": "new-user-123", // Profile created automatically }, } ); ``` ## Profile Summary The summary is a natural language description of the user, generated by analyzing their traits and memories: ``` Alex is an experienced software engineer who prefers concise, technical explanations. They work primarily with Python and have been exploring distributed systems. ``` Summaries are regenerated when: * High-priority traits change (name, language, expertise) * Enough time has passed since the last update (configurable) # Trait Schemas Source: https://docs.getprofile.org/concepts/trait-schemas Define what GetProfile extracts from conversations ## What are Trait Schemas? Trait schemas define the structure and extraction rules for user traits. They tell GetProfile: * What traits to look for * How to extract them * How to inject them into prompts ## Schema Structure ```typescript theme={null} interface TraitSchema { key: string; // Unique identifier label?: string; // Human-readable name description?: string; // What this trait represents valueType: "string" | "number" | "boolean" | "array" | "enum"; enumValues?: string[]; // For enum types category?: string; // Grouping (communication, context, etc.) extraction: { enabled: boolean; promptSnippet?: string; // Hint for extraction confidenceThreshold: number; // Minimum confidence to store }; injection: { enabled: boolean; template?: string; // How to format in prompt priority: number; // Order in injection (higher = earlier) }; } ``` ## Default Schema GetProfile ships with a default schema at `config/traits/default.traits.json`: ```json theme={null} { "traits": [ { "key": "name", "label": "Name", "description": "User's name or preferred name", "valueType": "string", "category": "identity", "extraction": { "enabled": true, "promptSnippet": "Extract the user's name if they mention it", "confidenceThreshold": 0.9 }, "injection": { "enabled": true, "template": "User's name is {{value}}.", "priority": 10 } } ] } ``` ## Custom Schemas Create domain-specific schemas for your use case: ```json theme={null} { "traits": [ { "key": "subscription_tier", "valueType": "enum", "enumValues": ["free", "pro", "enterprise"], "extraction": { "enabled": true, "promptSnippet": "Infer subscription level from context" }, "injection": { "template": "Customer is on {{value}} plan." } }, { "key": "frustration_level", "valueType": "enum", "enumValues": ["calm", "frustrated", "angry"], "extraction": { "enabled": true, "promptSnippet": "Assess emotional state from tone" } } ] } ``` ```json theme={null} { "traits": [ { "key": "player_class", "valueType": "enum", "enumValues": ["warrior", "mage", "rogue", "healer"] }, { "key": "play_style", "valueType": "enum", "enumValues": ["aggressive", "defensive", "balanced"] }, { "key": "quest_progress", "valueType": "array", "extraction": { "promptSnippet": "Track completed quests mentioned" } } ] } ``` ## Per-Request Overrides Define traits dynamically for specific requests: ```typescript theme={null} const response = await client.chat.completions.create({ model: "gpt-5", messages: [{ role: "user", content: "Help me plan my trip" }], extra_body: { getprofile: { traits: [ { key: "travel_preferences", valueType: "string", extraction: { enabled: true, promptSnippet: "Extract travel style preferences", }, injection: { enabled: true, template: "User prefers: {{value}}", }, }, ], }, }, }); ``` Per-request traits completely replace system traits when provided (no merging). This gives full control per request. ## Extraction Configuration | Field | Description | | --------------------- | ------------------------------------- | | `enabled` | Whether to extract this trait | | `promptSnippet` | Hint added to extraction prompt | | `confidenceThreshold` | Minimum confidence to store (0.0-1.0) | ## Injection Configuration | Field | Description | | ---------- | ------------------------------------------ | | `enabled` | Whether to inject this trait | | `template` | Format string with `{{value}}` placeholder | | `priority` | Order in injection (higher = earlier) | # Traits Source: https://docs.getprofile.org/concepts/traits Structured user attributes with confidence scores ## What are Traits? Traits are structured attributes extracted from user conversations. Unlike unstructured memory, traits are: * **Typed**: String, number, boolean, array, or enum * **Labeled**: Each trait has a defined key and category * **Scored**: Confidence level from 0.0 to 1.0 * **Tracked**: Source messages are recorded (optionally) ## Trait Structure ```typescript theme={null} interface Trait { id: string; profileId: string; key: string; // e.g., "preferred_language" category: string | null; // e.g., "communication" valueType: "string" | "number" | "boolean" | "array" | "enum"; value: unknown; confidence: number; // 0.0 - 1.0 source: "extracted" | "manual" | "inferred"; sourceMessageIds: string[]; createdAt: Date; updatedAt: Date; } ``` ## Default Traits GetProfile includes these default traits out of the box: | Trait | Type | Category | Description | | --------------------- | ------ | ------------- | ---------------------------------------- | | `name` | string | identity | User's name or preferred name | | `preferred_language` | string | communication | Communication language | | `communication_style` | enum | communication | formal, casual, technical, simple | | `detail_preference` | enum | communication | brief, moderate, detailed | | `expertise_level` | enum | context | beginner, intermediate, advanced, expert | | `timezone` | string | context | User's timezone | | `interests` | array | preferences | Topics the user is interested in | | `current_goals` | array | context | What the user is working toward | Customize or extend this schema via JSON configuration files or dynamically per-request. See [Trait Schema Configuration](/concepts/trait-schemas) for details. ## Confidence Scores Confidence represents how certain GetProfile is about a trait value: | Score | Meaning | Example | | ------- | ------------------ | ---------------------------------- | | 0.9+ | Explicit statement | "My name is Alex" | | 0.7-0.9 | Strong indication | Uses technical jargon consistently | | 0.5-0.7 | Moderate inference | Seems to prefer detailed responses | | \< 0.5 | Weak inference | Might be interested in Python | Traits with low confidence are still stored but may not be injected into prompts. Configure the `confidenceThreshold` in your trait schema. ## Trait Sources | Source | Description | | ----------- | ------------------------------------------ | | `extracted` | Automatically extracted from conversations | | `manual` | Set directly via API | | `inferred` | Derived from other traits or patterns | ## Trait Updates When new information conflicts with existing traits: 1. **Higher confidence wins**: New extraction with 0.8 confidence overrides existing 0.5 2. **Manual takes priority**: Manually set traits are preserved unless explicitly updated 3. **Contradictions delete**: If new info directly contradicts a trait, it's deleted ## Example: Trait Extraction Given this conversation: ``` User: Hi, I'm Alex and I've been coding in Python for about 5 years now. I prefer when explanations are concise and technical. ``` GetProfile extracts: ```json theme={null} [ { "key": "name", "value": "Alex", "confidence": 0.95, "action": "create" }, { "key": "expertise_level", "value": "advanced", "confidence": 0.75, "action": "create" }, { "key": "communication_style", "value": "technical", "confidence": 0.85, "action": "create" }, { "key": "detail_preference", "value": "brief", "confidence": 0.7, "action": "create" } ] ``` # Configuration Overview Source: https://docs.getprofile.org/configuration/overview Configure GetProfile for your environment - works with OpenAI, Anthropic, and any OpenAI-compatible provider ## Configuration File GetProfile uses a JSON configuration file. Create `config/getprofile.json`: ```json theme={null} { "database": { "url": "${DATABASE_URL}", "poolSize": 10 }, "llm": { "provider": "openai", "apiKey": "${LLM_API_KEY}", "model": "gpt-5-mini" }, "upstream": { "provider": "openai", "apiKey": "${LLM_API_KEY}" }, "memory": { "maxMessagesPerProfile": 1000, "extractionEnabled": true, "summarizationInterval": 60 }, "traits": { "schemaPath": "./config/traits/default.traits.json", "extractionEnabled": true, "defaultTraitsEnabled": true, "allowRequestOverride": true }, "server": { "port": 3100, "host": "0.0.0.0" } } ``` **Provider-Agnostic**: GetProfile works with OpenAI, Anthropic, OpenRouter, or any OpenAI-compatible API. Just change the `provider` field and model name. ## Environment Variables **Minimalistic Approach**: GetProfile only uses environment variables for **secrets** and **high-level server settings**. All other configuration goes in `config/getprofile.json`. Configuration values can reference environment variables using `${VAR_NAME}` syntax. ### Required Secrets | Variable | Description | | -------------- | ------------------------------------------------------------------------------ | | `DATABASE_URL` | PostgreSQL connection string | | `LLM_API_KEY` | API key for your LLM provider (works with OpenAI, Anthropic, OpenRouter, etc.) | **Provider-specific keys** (optional, fallback to `LLM_API_KEY`): * `OPENAI_API_KEY` - OpenAI-specific key * `ANTHROPIC_API_KEY` - Anthropic-specific key ### Server Settings | Variable | Default | Description | | -------- | ------- | ----------------- | | `PORT` | 3100 | Proxy server port | | `HOST` | 0.0.0.0 | Proxy server host | ### Optional Secrets | Variable | Default | Description | | -------------------- | ------- | -------------------------------- | | `GETPROFILE_API_KEY` | - | API key for proxy authentication | **Environment Variable Support**: All configuration can be set via environment variables for backward compatibility and deployment flexibility. The config file (`config/getprofile.json`) is the recommended approach for structured configuration, but environment variables take precedence when both are set. Environment variables that map to config file settings: * `UPSTREAM_API_KEY`, `UPSTREAM_BASE_URL`, `UPSTREAM_PROVIDER` → `upstream` section * `GETPROFILE_MAX_MESSAGES` → `memory.maxMessagesPerProfile` * `GETPROFILE_SUMMARY_INTERVAL` → `memory.summarizationInterval` * `LLM_API_KEY`, `LLM_PROVIDER`, `LLM_MODEL`, `LLM_BASE_URL` → `llm` section * `PORT`, `HOST` → `server` section Priority order: Environment variables > Config file > Defaults ## Configuration Sections ```json theme={null} { "database": { "url": "postgresql://user:pass@localhost:5432/getprofile", "poolSize": 10 } } ``` | Field | Type | Description | | ---------- | ------ | ---------------------------------- | | `url` | string | PostgreSQL connection string | | `poolSize` | number | Connection pool size (default: 10) | LLM used for internal processing (extraction, summarization). **OpenAI Example:** ```json theme={null} { "llm": { "provider": "openai", "apiKey": "${LLM_API_KEY}", "model": "gpt-5-mini" } } ``` **Anthropic Example:** ```json theme={null} { "llm": { "provider": "anthropic", "apiKey": "${ANTHROPIC_API_KEY}", "model": "claude-4-5-sonnet" } } ``` **OpenRouter Example:** ```json theme={null} { "llm": { "provider": "custom", "apiKey": "${LLM_API_KEY}", "baseUrl": "https://openrouter.ai/api/v1", "model": "anthropic/claude-4.5-sonnet" } } ``` | Field | Type | Description | | ---------- | ------ | ------------------------------------------ | | `provider` | string | `openai`, `anthropic`, or `custom` | | `apiKey` | string | API key for the provider | | `model` | string | Model to use for extraction | | `baseUrl` | string | Custom API endpoint (for custom providers) | LLM provider where chat completion requests are forwarded. Can be different from the LLM used for extraction. **Same as LLM (default):** ```json theme={null} { "upstream": { "provider": "openai", "apiKey": "${LLM_API_KEY}" } } ``` **Different provider:** ```json theme={null} { "upstream": { "provider": "anthropic", "apiKey": "${ANTHROPIC_API_KEY}" } } ``` **Per-request override via headers:** Clients can override the upstream provider using headers: * `X-Upstream-Provider`: `openai`, `anthropic`, or `custom` * `X-Upstream-Key`: API key for that provider * `X-Upstream-Base-URL`: Custom base URL (optional) ```json theme={null} { "memory": { "maxMessagesPerProfile": 1000, "extractionEnabled": true, "summarizationInterval": 60, "retentionDays": null } } ``` | Field | Type | Description | | ----------------------- | ------- | ------------------------------------------ | | `maxMessagesPerProfile` | number | Soft limit triggering cleanup | | `extractionEnabled` | boolean | Enable memory extraction | | `summarizationInterval` | number | Minutes between summary updates | | `retentionDays` | number | Auto-delete old messages (null = disabled) | ```json theme={null} { "traits": { "schemaPath": "./config/traits/default.traits.json", "extractionEnabled": true, "defaultTraitsEnabled": true, "allowRequestOverride": true } } ``` | Field | Type | Description | | ---------------------- | ------- | ------------------------- | | `schemaPath` | string | Path to trait schema JSON | | `extractionEnabled` | boolean | Enable trait extraction | | `defaultTraitsEnabled` | boolean | Include default traits | | `allowRequestOverride` | boolean | Allow per-request traits | ```json theme={null} { "server": { "port": 3100, "host": "0.0.0.0" } } ``` # Prompt Configuration Source: https://docs.getprofile.org/configuration/prompts Customize extraction and summarization prompts ## Custom Prompts GetProfile uses LLM prompts for extraction and summarization. You can customize these by providing your own prompt files. ```json theme={null} { "prompts": { "extractionPath": "./config/prompts/extraction.md", "traitExtractionPath": "./config/prompts/trait-extraction.md", "summarizationPath": "./config/prompts/summarization.md" } } ``` ## Memory Extraction Prompt Used to extract facts, preferences, and context from conversations. ### Template Variables | Variable | Description | | ------------------ | --------------------------- | | `{{conversation}}` | The conversation to analyze | ### Default Prompt ```markdown theme={null} # Memory Extraction Prompt You are a memory extraction assistant. Your job is to analyze conversations and extract important facts, preferences, and context about the user. ## Instructions Given the following conversation, extract: 1. **Facts**: Concrete information (name, job, location, etc.) 2. **Preferences**: Things the user likes, dislikes, or prefers 3. **Events**: Notable events or experiences mentioned 4. **Context**: Situational information for ongoing conversations ## Rules - Only extract information explicitly stated or strongly implied - Each memory should be self-contained and understandable - Assign importance (0.0-1.0) based on likely future relevance - Do not extract information about the AI assistant ## Output Format Return a JSON array: \`\`\`json [ { "content": "User works as a software engineer at a startup", "type": "fact", "importance": 0.8 } ] \`\`\` ## Conversation {{conversation}} ``` ## Trait Extraction Prompt Used to extract structured traits based on your schema. ### Template Variables | Variable | Description | | -------------------- | ----------------------------- | | `{{trait_schema}}` | Your trait schema definitions | | `{{current_traits}}` | User's existing traits | | `{{conversation}}` | The conversation to analyze | ### Default Prompt ```markdown theme={null} # Trait Extraction Prompt You are a user profiling assistant. Extract and update structured traits based on conversations. ## Available Traits {{trait_schema}} ## Current User Profile {{current_traits}} ## Instructions Determine if any traits should be: - **Created**: New trait not previously known - **Updated**: Existing trait needs revision - **Deleted**: Previous trait is contradicted ## Rules - Only update traits with sufficient evidence - Provide confidence score (0.0-1.0) - Higher confidence for explicit statements - If unsure, prefer not updating ## Output Format \`\`\`json [ { "key": "expertise_level", "value": "advanced", "confidence": 0.75, "action": "update", "reason": "Used technical terminology" } ] \`\`\` ## Conversation {{conversation}} ``` ## Summarization Prompt Used to generate natural language profile summaries. ### Template Variables | Variable | Description | | -------------- | --------------- | | `{{traits}}` | User's traits | | `{{memories}}` | Recent memories | ### Default Prompt ```markdown theme={null} # Profile Summarization Prompt Create a concise summary of a user based on their traits and memories. ## User Traits {{traits}} ## Recent Memories {{memories}} ## Instructions Write a 2-3 sentence summary describing who this user is. Include: - Key identifying information (if known) - Communication preferences - Relevant context for conversations ## Rules - Be concise (100-200 tokens max) - Write naturally, as if describing to a colleague - Focus on conversation-relevant information - Do not include speculative information ## Output Write the summary directly, no JSON formatting. ``` ## Best Practices Give clear extraction hints in `promptSnippet` for better trait extraction. Use appropriate `confidenceThreshold` values—higher for critical traits. Test your custom prompts with sample conversations before deploying. Monitor extraction quality and refine prompts based on results. # Trait Schema Configuration Source: https://docs.getprofile.org/configuration/trait-schemas Define custom trait extraction rules ## Schema File Location Place your trait schema in `config/traits/` and reference it in your configuration: ```json theme={null} { "traits": { "schemaPath": "./config/traits/my-schema.traits.json" } } ``` ## Schema Format ```json theme={null} { "traits": [ { "key": "trait_name", "label": "Human Readable Name", "description": "What this trait represents", "valueType": "string", "category": "category_name", "extraction": { "enabled": true, "promptSnippet": "Extraction hint for the LLM", "confidenceThreshold": 0.5 }, "injection": { "enabled": true, "template": "User prefers {{value}}.", "priority": 5 } } ] } ``` ## Value Types Free-form text value. ```json theme={null} { "key": "name", "valueType": "string" } ``` Extracted value: `"Alex"` Numeric value. ```json theme={null} { "key": "years_experience", "valueType": "number" } ``` Extracted value: `5` True/false value. ```json theme={null} { "key": "prefers_dark_mode", "valueType": "boolean" } ``` Extracted value: `true` One of predefined values. ```json theme={null} { "key": "expertise_level", "valueType": "enum", "enumValues": ["beginner", "intermediate", "advanced", "expert"] } ``` Extracted value: `"advanced"` List of values. ```json theme={null} { "key": "interests", "valueType": "array" } ``` Extracted value: `["Python", "ML", "DevOps"]` ## Extraction Settings ### promptSnippet A hint added to the extraction prompt to help the LLM understand what to look for: ```json theme={null} { "key": "communication_style", "extraction": { "promptSnippet": "Assess if user prefers formal, casual, technical, or simple communication based on their language and requests" } } ``` ### confidenceThreshold Minimum confidence score required to store the trait: | Threshold | Use Case | | --------- | ------------------------------ | | 0.9 | High-stakes traits (name, PII) | | 0.7 | Important preferences | | 0.5 | General traits (default) | | 0.3 | Experimental/weak signals | ## Injection Settings ### template Format string for injecting the trait into prompts. Use `{{value}}` placeholder: ```json theme={null} { "key": "name", "injection": { "template": "The user's name is {{value}}. Address them by name when appropriate." } } ``` For array values: ```json theme={null} { "key": "interests", "injection": { "template": "User is interested in: {{value}}" } } // Renders as: "User is interested in: Python, ML, DevOps" ``` ### priority Higher priority traits appear earlier in the injected context: | Priority | Typical Traits | | -------- | ---------------------- | | 10 | Name, language | | 8-9 | Communication style | | 5-7 | Expertise, preferences | | 1-4 | Interests, goals | ## Full Example ```json theme={null} { "traits": [ { "key": "role", "label": "Professional Role", "description": "User's job title or professional role", "valueType": "string", "category": "identity", "extraction": { "enabled": true, "promptSnippet": "Extract job title, role, or profession if mentioned", "confidenceThreshold": 0.8 }, "injection": { "enabled": true, "template": "User works as a {{value}}.", "priority": 8 } }, { "key": "tech_stack", "label": "Technology Stack", "description": "Technologies and tools the user works with", "valueType": "array", "category": "context", "extraction": { "enabled": true, "promptSnippet": "Identify programming languages, frameworks, and tools mentioned", "confidenceThreshold": 0.6 }, "injection": { "enabled": true, "template": "User's tech stack includes: {{value}}", "priority": 6 } } ] } ``` # Docker Deployment Source: https://docs.getprofile.org/deployment/docker Deploy GetProfile with Docker Compose ## Quick Start ```bash theme={null} # Clone the repository git clone https://github.com/getprofile/getprofile.git cd getprofile # Configure environment cp .env.docker.example .env # Edit .env with your API keys # Start all services (source .env to handle long API keys correctly) source .env && export LLM_API_KEY && docker compose -f docker/docker-compose.yml up -d ``` ## Services The Docker Compose setup includes: | Service | Port | Description | | -------- | ---- | ------------------------ | | `server` | 3100 | GetProfile LLM proxy | | `db` | 5432 | PostgreSQL with pgvector | ## docker-compose.yml The actual docker-compose.yml is located at `docker/docker-compose.yml`: ```yaml theme={null} services: server: build: context: .. dockerfile: docker/Dockerfile.server ports: - "3100:3100" environment: # Database - DATABASE_URL=postgresql://getprofile:password@db:5432/getprofile # LLM Provider (for extraction/summarization) - LLM_API_KEY=${LLM_API_KEY} # Upstream LLM (defaults to LLM_API_KEY) - UPSTREAM_API_KEY=${UPSTREAM_API_KEY:-${LLM_API_KEY}} - UPSTREAM_BASE_URL=${UPSTREAM_BASE_URL:-https://api.openai.com/v1} # Server auth (optional - if not set, allows all requests) - GETPROFILE_API_KEY=${GETPROFILE_API_KEY:-} # Retention and summary tuning (optional) - GETPROFILE_MAX_MESSAGES=${GETPROFILE_MAX_MESSAGES:-1000} - GETPROFILE_SUMMARY_INTERVAL=${GETPROFILE_SUMMARY_INTERVAL:-60} # Rate limiting (optional, 0 to disable) - GETPROFILE_RATE_LIMIT=${GETPROFILE_RATE_LIMIT:-60} # Server - PORT=${PORT:-3100} - HOST=${HOST:-0.0.0.0} depends_on: db: condition: service_healthy db: image: pgvector/pgvector:pg16 environment: - POSTGRES_USER=getprofile - POSTGRES_PASSWORD=password - POSTGRES_DB=getprofile volumes: - pgdata:/var/lib/postgresql/data ports: - "5432:5432" healthcheck: test: ["CMD-SHELL", "pg_isready -U getprofile"] interval: 5s timeout: 5s retries: 5 volumes: pgdata: ``` ## Environment Variables Create a `.env` file in the repository root using `.env.docker.example` as a template: ```bash theme={null} # Required LLM_API_KEY=sk-your-key-here # Optional - Server Authentication GETPROFILE_API_KEY=your-secret-key-here # If not set, allows all requests # Optional - Upstream LLM Configuration UPSTREAM_API_KEY=sk-different-key # Defaults to LLM_API_KEY UPSTREAM_BASE_URL=https://api.openai.com/v1 # Optional - Message Retention and Summary GETPROFILE_MAX_MESSAGES=1000 # Max messages per profile GETPROFILE_SUMMARY_INTERVAL=60 # Summary refresh interval (minutes) # Optional - Rate Limiting GETPROFILE_RATE_LIMIT=60 # Requests per minute (0 to disable) # Optional - Server Configuration PORT=3100 HOST=0.0.0.0 ``` See [docker/README.md](https://github.com/getprofile/getprofile/blob/main/docker/README.md) for complete documentation. ## Commands ```bash theme={null} # Start services (source .env to handle long API keys correctly) source .env && export LLM_API_KEY && docker compose -f docker/docker-compose.yml up -d # View logs docker compose -f docker/docker-compose.yml logs -f server # Stop services docker compose -f docker/docker-compose.yml down # Stop and remove volumes (deletes data!) docker compose -f docker/docker-compose.yml down -v # Restart after .env changes docker compose -f docker/docker-compose.yml down source .env && export LLM_API_KEY && docker compose -f docker/docker-compose.yml up -d # Rebuild after code changes docker compose -f docker/docker-compose.yml build --no-cache source .env && export LLM_API_KEY && docker compose -f docker/docker-compose.yml up -d ``` ## Production Considerations The default docker-compose.yml is for development. For production: ### Security * Change default database password * Use secrets management for API keys * Enable TLS/HTTPS * Set up proper network isolation ### Persistence * Use external PostgreSQL for production * Set up database backups * Consider Redis for caching ### Scaling ```yaml theme={null} services: server: deploy: replicas: 3 # ... rest of config ``` ### Health Checks ```bash theme={null} # Check server health curl http://localhost:3100/health # Expected response { "status": "ok", "version": "0.1.0", "timestamp": "2024-01-01T00:00:00.000Z" } ``` ## Troubleshooting ### Environment Variables Not Loading If you experience issues with environment variables (especially long API keys) appearing truncated in containers: **Problem**: Docker Compose may not correctly parse long API keys from the `.env` file directly, causing errors like "UPSTREAM\_API\_KEY or LLM\_API\_KEY environment variable is required". **Solution**: Source the `.env` file and export variables before running docker compose: ```bash theme={null} # Stop containers docker compose -f docker/docker-compose.yml down # Source .env and start containers source .env && export LLM_API_KEY && docker compose -f docker/docker-compose.yml up -d ``` **Verification**: Check that the API key is loaded correctly: ```bash theme={null} docker compose -f docker/docker-compose.yml exec server sh -c 'echo "API Key length: ${#LLM_API_KEY}"' ``` You should see the correct length (e.g., 164 characters for OpenAI keys), not just 6. ### Database Migrations Not Running If the server fails to start with migration errors: ```bash theme={null} # Check server logs docker compose -f docker/docker-compose.yml logs server # Manually run migrations docker compose -f docker/docker-compose.yml exec server sh -c "cd /app/packages/db && pnpm drizzle-kit migrate" ``` ### Container Build Failures If you encounter module resolution errors during build: ```bash theme={null} # Clean rebuild docker compose -f docker/docker-compose.yml down -v docker compose -f docker/docker-compose.yml build --no-cache source .env && export LLM_API_KEY && docker compose -f docker/docker-compose.yml up -d ``` For more troubleshooting help, see [docker/README.md](https://github.com/getprofile/getprofile/blob/main/docker/README.md). # Self-Hosting Guide Source: https://docs.getprofile.org/deployment/self-hosting Run GetProfile on your own infrastructure ## Requirements * **Node.js** 20+ * **PostgreSQL** 15+ with pgvector extension * **pnpm** package manager ## Installation ```bash theme={null} git clone https://github.com/getprofile/getprofile.git cd getprofile ``` ```bash theme={null} pnpm install ``` Create a database with pgvector: ```sql theme={null} CREATE DATABASE getprofile; \c getprofile CREATE EXTENSION vector; ``` ```bash theme={null} cp .env.example .env ``` Edit `.env`: ```bash theme={null} DATABASE_URL=postgresql://user:pass@localhost:5432/getprofile LLM_API_KEY=sk-your-key GETPROFILE_API_KEY=your-server-key # optional GETPROFILE_MAX_MESSAGES=1000 # optional, prune old messages beyond this GETPROFILE_SUMMARY_INTERVAL=60 # optional, minutes between summary refresh GETPROFILE_RATE_LIMIT=60 # optional, requests per minute (0 to disable) ``` ```bash theme={null} pnpm db:migrate ``` ```bash theme={null} pnpm db:seed:sample ``` Seeds a demo profile for smoke-testing the server. ```bash theme={null} pnpm build ``` ```bash theme={null} # Start server cd apps/server && pnpm start ``` ## CI / Automation In CI pipelines, run migrations before tests/builds so the schema stays current: ```bash theme={null} pnpm ci:prepare # runs migrations + sample seed (optional for local preview) ``` Use a real DATABASE\_URL for staging/production; the seed is safe to skip if you prefer a clean database. ## Process Management Use a process manager like PM2 for production: ```bash theme={null} # Install PM2 npm install -g pm2 # Start server pm2 start apps/server/dist/index.js --name getprofile-server # Save process list pm2 save # Enable startup script pm2 startup ``` ## Reverse Proxy ### Nginx ```nginx theme={null} server { listen 80; server_name api.getprofile.yourdomain.com; location / { proxy_pass http://localhost:3100; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_cache_bypass $http_upgrade; # SSE support proxy_buffering off; proxy_read_timeout 86400; } } ``` ### Caddy ``` api.getprofile.yourdomain.com { reverse_proxy localhost:3100 } ``` ## Database Maintenance ### Backups ```bash theme={null} # Daily backup script pg_dump -U getprofile getprofile > backup_$(date +%Y%m%d).sql # Restore psql -U getprofile getprofile < backup_20240101.sql ``` ### Migrations ```bash theme={null} # Generate new migration after schema changes pnpm db:generate # Apply migrations pnpm db:migrate # View database with Drizzle Studio pnpm db:studio ``` ## Monitoring ### Health Endpoint ```bash theme={null} curl http://localhost:3100/health ``` ### Logs ```bash theme={null} # PM2 logs pm2 logs getprofile-server # Docker logs docker compose logs -f server ``` ## Security Checklist * Use HTTPS in production * Set strong database password * Use environment variables for secrets * Enable rate limiting * Set up firewall rules * Configure database backups * Monitor for security updates # How It Works Source: https://docs.getprofile.org/how-it-works Understanding GetProfile's architecture and data flow ## Architecture Overview GetProfile provides the **LLM Proxy** for automatic, transparent integration with your existing OpenAI-compatible applications. ### Proxy Flow (Automatic) The proxy sits between your application and the LLM provider, enriching every request with user context automatically. ``` ┌───────────────┐ ┌──────────────────────────────────┐ ┌─────────────────┐ │ │ │ GetProfile Proxy │ │ │ │ Your App │────▶│ │────▶│ LLM Provider │ │ │ │ 1. Load user profile │ │ (OpenAI, etc) │ │ │ │ 2. Retrieve relevant memories │ │ │ └───────────────┘ │ 3. Inject context into prompt │ └─────────────────┘ │ 4. Forward to LLM │ │ 5. Stream response back │ │ 6. Extract traits (background) │ └──────────────────────────────────┘ ``` ## Request Flow Your app sends a chat completion request to the GetProfile proxy with a user identifier. GetProfile looks up the user's profile using the `X-GetProfile-Id` header or `user` field. The proxy gathers: * User traits (name, preferences, expertise level) * Profile summary * Relevant memories from past conversations Context is injected into the system message: \`\`\` ## User Profile Alex is an experienced software engineer who prefers concise, technical explanations. ## User Attributes - Communication style: technical - Expertise level: advanced ## Relevant Context - User mentioned working on a microservices migration last week \`\`\` The enriched request is sent to the upstream LLM provider. The LLM response is streamed back to your app in real-time. After the response completes, GetProfile asynchronously: * Extracts new traits from the conversation * Stores relevant memories * Updates the profile summary if needed ## Three-Layer Architecture ``` ┌─────────────────────────────────────────────────────────────────┐ │ PRESENTATION LAYER │ ├───────────────┬───────────────┬─────────────────────────────────┤ │ LLM Proxy │ Profile API │ Dashboard │ │ (automatic) │ (SDK or REST) │ (Cloud version only) │ └───────────────┴───────────────┴─────────────────────────────────┘ │ ┌─────────────────────────────────────────────────────────────────────┐ │ CORE ENGINE LAYER │ ├────────────────────────────┬───────────────────────────────────────┤ │ Memory Engine │ Trait Engine │ │ - Message storage │ - Schema management │ │ - Fact extraction │ - Trait extraction │ │ - Memory retrieval │ - Confidence scoring │ │ - Summarization │ - Profile summary generation │ └────────────────────────────┴───────────────────────────────────────┘ │ ┌─────────────────────────────────────────────────────────────────────┐ │ STORAGE LAYER │ ├────────────────────────────┬───────────────────────────────────────┤ │ PostgreSQL │ (Optional) Redis │ │ - Profiles │ - Profile summary cache │ │ - Traits │ - Hot memory cache │ │ - Messages │ - Rate limiting │ │ - Memories (pgvector) │ │ └────────────────────────────┴───────────────────────────────────────┘ ``` All integration methods (Proxy, API, Dashboard) share the same core engine and storage, ensuring consistent behavior regardless of how you integrate. ## Key Components Responsible for: - Storing conversation messages - Extracting facts and memories using LLM - Retrieving relevant memories for context - Generating profile summaries Responsible for: - Loading trait schemas (default + custom) - Extracting structured traits from conversations - Managing confidence scores - Building injection context Orchestrates: - Profile creation and lookup - Context assembly for requests * Coordinating memory and trait engines ## Choosing an Integration | If you need... | Use | | ---------------------------------- | ---------------------------------------------- | | Automatic memory on every LLM call | [LLM Proxy](/openai-compatibility) | | Backend integration in Node.js | [JavaScript SDK](/client-libraries/javascript) | | REST API for custom clients | [Profile API](/api-reference/overview) | See the [Integration Options](/integrations/overview) guide for detailed comparison. # Introduction Source: https://docs.getprofile.org/introduction User profile and long-term memory for your AI agent ## The Problem LLMs are stateless. Every conversation starts from scratch. Your AI assistant doesn't remember: * User preferences ("I prefer concise answers") * Past context ("We discussed this project last week") * Personal details ("I'm a Python developer working at a startup") This makes AI interactions feel impersonal and repetitive. ## The Solution GetProfile is a **drop-in LLM proxy** that automatically: Conversations between users and your AI Structured traits and memories using LLM analysis Relevant context into every prompt User profiles continuously in the background ## Multiple Integration Options Change your OpenAI base URL for automatic memory injection Programmatic access from Node.js/TypeScript ### Proxy Integration (Automatic) Just change your OpenAI base URL. That's it. ```typescript theme={null} // Before: Stateless AI const client = new OpenAI({ apiKey: "sk-..." }); // After: AI with memory const client = new OpenAI({ apiKey: "gp_...", // Your GetProfile API key baseURL: "https://api.yourserver.com/v1", // Or your self-hosted instance defaultHeaders: { "X-GetProfile-Id": userId, // Your app's user ID "X-Upstream-Key": "sk-...", // Your OpenAI key }, }); // Same API, now with persistent memory const response = await client.chat.completions.create({ model: "gpt-5", messages: [{ role: "user", content: "How should I refactor this?" }], }); ``` ## Key Features Unlike generic memory solutions that store blobs of text, GetProfile extracts **typed traits** with confidence scores: ```json theme={null} { "name": { "value": "Alex", "confidence": 0.95 }, "expertise_level": { "value": "advanced", "confidence": 0.8 }, "communication_style": { "value": "technical", "confidence": 0.7 } } ``` * OpenAI-compatible proxy — works with any OpenAI SDK * No code changes — just update your base URL * Streaming support — full SSE streaming passthrough Define what matters for your app with JSON configuration files. * Apache 2.0 licensed — use it anywhere * Self-host with Docker — your data stays with you * Transparent — audit the code, understand what's happening ## What's Next? Get up and running in 5 minutes Understand the architecture Choose the right integration for your use case Programmatic access from Node.js/TypeScript # Quickstart Source: https://docs.getprofile.org/quickstart Get GetProfile running in under 5 minutes ## Prerequisites * Docker and Docker Compose * An LLM API key (works with OpenAI, Anthropic, OpenRouter, or any OpenAI-compatible provider) ## Option 1: Docker (Recommended) ```bash theme={null} git clone https://github.com/getprofile/getprofile.git cd getprofile ``` ```bash theme={null} cp .env.docker.example .env ``` Edit `.env` and add your LLM API key: ```bash theme={null} # Works with any provider (OpenAI, Anthropic, OpenRouter, etc.) LLM_API_KEY=sk-your-key-here # Or use provider-specific keys # OPENAI_API_KEY=sk-... # ANTHROPIC_API_KEY=sk-... ``` **Provider Configuration**: Edit `config/getprofile.json` to choose your provider: ```json theme={null} { "llm": { "provider": "openai", // or "anthropic" or "custom" "model": "gpt-5-mini" // or "claude-4-5-sonnet" } } ``` The `.env.docker.example` file is optimized for Docker deployment. For local development without Docker, use `.env.example` instead. ```bash theme={null} source .env && export LLM_API_KEY && docker compose -f docker/docker-compose.yml up -d ``` This starts: * GetProfile Proxy on `http://localhost:3100` * PostgreSQL database **Note:** We source the `.env` file before starting to ensure long API keys are loaded correctly. Database migrations run automatically on first start. Monitor logs with: ```bash theme={null} docker compose -f docker/docker-compose.yml logs -f proxy ``` If you want to protect your proxy with an API key, set it in your `.env`: ```bash theme={null} GETPROFILE_API_KEY=your-secret-key-here ``` If not set, the proxy will accept all requests (useful for local development). **Configuration**: You can configure GetProfile via `config/getprofile.json` or environment variables. Environment variables take precedence. See [Configuration](/configuration/overview) for details. After changing `.env`, restart services: ```bash theme={null} docker compose -f docker/docker-compose.yml down source .env && export LLM_API_KEY && docker compose -f docker/docker-compose.yml up -d ``` ```bash theme={null} curl http://localhost:3100/health ``` You should see: ```json theme={null} { "status": "ok", "version": "0.1.0", "timestamp": "2024-01-01T00:00:00.000Z" } ``` ## Option 2: Local Development * Node.js 20+ * pnpm * PostgreSQL 15+ (pgvector enabled) ````bash git clone https://github.com/getprofile/getprofile.git && cd theme={null} getprofile && pnpm install ``` ```bash cp .env.example .env ```` Edit `.env` with your `DATABASE_URL` and `LLM_API_KEY`: ```bash theme={null} DATABASE_URL=postgresql://user:pass@localhost:5432/getprofile LLM_API_KEY=sk-your-key-here # Works with OpenAI, Anthropic, etc. ``` Optional: Set `GETPROFILE_API_KEY` to require authentication on the proxy. Other settings like rate limiting, message retention, and provider configuration are now in `config/getprofile.json`. See [Configuration](/configuration/overview). `bash pnpm db:migrate ` `bash pnpm db:seed:sample ` Seeds a demo profile for smoke-testing the dashboard and API. If you want to protect your proxy with an API key, set it in your `.env`: `bash GETPROFILE_API_KEY=your-secret-key-here ` If not set, the proxy will accept all requests (useful for local development). ```bash theme={null} pnpm dev ``` ## Using the Proxy Once running, update your OpenAI client to use GetProfile. Works with **any LLM provider**: ```typescript TypeScript theme={null} import { GetProfileClient } from "@getprofile/sdk-js"; const client = new GetProfileClient({ apiKey: process.env.GETPROFILE_API_KEY || "not-needed-for-local", baseURL: "http://localhost:3100/v1", defaultHeaders: { "X-GetProfile-Id": "user-123", "X-Upstream-Key": process.env.OPENAI_API_KEY, "X-Upstream-Provider": "openai", }, }); const response = await client.chat.completions.create({ model: "gpt-5-mini", messages: [{ role: "user", content: "Hello!" }], }); ``` ```typescript TypeScript theme={null} import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.GETPROFILE_API_KEY || "not-needed-for-local", baseURL: "http://localhost:3100/v1", defaultHeaders: { "X-GetProfile-Id": "user-123", "X-Upstream-Key": process.env.OPENAI_API_KEY, "X-Upstream-Provider": "openai", }, }); const response = await client.chat.completions.create({ model: "gpt-5-mini", messages: [{ role: "user", content: "Hello!" }], }); ``` ```python Python theme={null} from openai import OpenAI import os client = OpenAI( api_key=os.getenv("GETPROFILE_API_KEY", "not-needed-for-local"), base_url="http://localhost:3100/v1", default_headers={ "X-GetProfile-Id": "user-123", "X-Upstream-Key": os.getenv("OPENAI_API_KEY"), "X-Upstream-Provider": "openai", }, ) response = client.chat.completions.create( model="gpt-5-mini", messages=[{"role": "user", "content": "Hello!"}], ) ``` **Headers**: Provider headers (`X-Upstream-Provider`, `X-Upstream-Key`) override the config file. If not provided, GetProfile uses the default provider configured in `config/getprofile.json`. ## Customizing Extraction GetProfile includes default trait schemas and prompts in the `config/` directory: ``` config/ ├── getprofile.example.json # Main configuration template ├── prompts/ # LLM extraction prompts │ ├── extraction.md # Memory extraction prompt │ ├── summarization.md # Profile summarization prompt │ └── trait-extraction.md # Trait extraction prompt └── traits/ # Trait schema definitions └── default.traits.json # Default trait schema ``` ### Customizing Traits Edit `config/traits/default.traits.json` to define what GetProfile extracts from conversations: ```json theme={null} { "traits": [ { "key": "communication_style", "valueType": "enum", "enumValues": ["technical", "casual", "formal"], "extraction": { "promptSnippet": "Identify the user's preferred communication style" }, "injection": { "template": "User prefers {{value}} communication" } } ] } ``` ### Customizing Prompts Edit the markdown files in `config/prompts/` to change how GetProfile: * Extracts memories from conversations (`extraction.md`) * Generates profile summaries (`summarization.md`) * Identifies trait values (`trait-extraction.md`) **For Docker:** After modifying config files, rebuild and restart: ```bash theme={null} docker compose -f docker/docker-compose.yml up -d --build ``` ## Next Steps Understand the architecture Compare proxy vs SDK Customize what GetProfile extracts Programmatic access from Node.js/TypeScript # Adaptive Tutor / Learning Companion Source: https://docs.getprofile.org/use-cases/adaptive-tutor AI tutors that adapt to each student's learning style and progress ## Scenario AI tutor for language, math, or coding inside a learning platform that needs to adapt to individual student needs. ## Extraction From student questions, answers, and exercises, GetProfile keeps traits like: * `skill_level` per topic: vocab, grammar, integrals, recursion… * `learning_style`: prefers step-by-step vs big-picture explanations * `common_mistakes[]`: typical grammar/calc errors * `motivation_pattern`: often gives up early vs pushes through It updates these as the student improves or regresses. ## Injection Before each tutoring LLM call, GetProfile injects: * Profile summary: * "Intermediate grammar, weak with conditionals; prefers concrete examples; often confused by abstract explanations." * A few **targeted memories**: * last explanation that worked, * last couple of failed attempts on the same concept. ## Impact The tutor can: * Choose the right difficulty and explanation style. * Remind the student of previous successful strategies. * Avoid re-trying explanations that already failed. ## Implementation ```typescript TypeScript theme={null} import OpenAI from 'openai'; const client = new OpenAI({ apiKey: process.env.GETPROFILE_API_KEY, baseURL: 'https://api.yourserver.com/v1', defaultHeaders: { 'X-GetProfile-Id': "student-456", 'X-Upstream-Key': process.env.OPENAI_API_KEY, }, }); // Tutoring session const response = await client.chat.completions.create({ model: 'gpt-5', messages: [ { role: 'system', content: 'You are a patient tutor. Adapt your explanations to the student\'s learning style.', }, { role: 'user', content: 'I still don\'t understand conditional statements in Python.', }, ], }); // GetProfile injects student's skill level, learning style, and past attempts ``` ## Trait Schema Example ```json theme={null} { "skill_level": { "type": "object", "description": "Skill level per topic", "additionalProperties": { "type": "string", "enum": ["beginner", "intermediate", "advanced"] } }, "learning_style": { "type": "string", "enum": ["step-by-step", "big-picture", "visual", "hands-on"], "description": "Preferred learning approach" }, "common_mistakes": { "type": "array", "items": { "type": "string" }, "description": "Topics or concepts the student frequently struggles with" }, "motivation_pattern": { "type": "string", "enum": ["persistent", "gives-up-early", "needs-encouragement"], "description": "How the student handles challenges" } } ``` ## Related Resources Store and retrieve learning milestones and breakthroughs Update skill levels as students progress # Customer Support Bot That Actually "Remembers" You Source: https://docs.getprofile.org/use-cases/customer-support-bot Support chatbots with persistent user context and preferences ## Scenario SaaS product with an in-app / website support chatbot that needs to remember user context across conversations. ## Extraction From chats, tickets, and events, GetProfile keeps a live profile per end-user: * `plan_tier`: free / pro / enterprise * `product_areas_used[]`: "billing", "integrations", "dashboards" * `recurring_issues[]`: "confused about usage limits", "OAuth failures" * `frustration_level`: low / medium / high * `tone_preference`: patient explanations vs short answers No one fills this out manually; it's inferred from conversations + app events. ## Injection When the user talks to the bot again, calls go through the GetProfile proxy: * It injects a short profile block into the system prompt: * "Pro-tier user, power user of integrations, recently frustrated about rate limits, prefers direct answers." * It also injects a couple of **recent, high-signal memories**: * last failed integration attempt, * last time support promised a fix. ## Impact The bot can: * Skip basic docs if user is a power user. * Preempt "we've had this issue before" with context. * Use calmer, more careful language if `frustration_level` is high. ## Implementation ```typescript TypeScript theme={null} import OpenAI from 'openai'; const client = new OpenAI({ apiKey: process.env.GETPROFILE_API_KEY, baseURL: 'https://api.yourserver.com/v1', defaultHeaders: { 'X-GetProfile-Id': userId, // Your app's user ID 'X-Upstream-Key': process.env.OPENAI_API_KEY, }, }); // Support bot conversation const response = await client.chat.completions.create({ model: 'gpt-5', messages: [ { role: 'system', content: 'You are a helpful support agent. Be concise and technical for power users.', }, { role: 'user', content: 'I keep getting rate limit errors on my API calls.', }, ], }); // GetProfile automatically injects user profile and relevant memories ``` ## Trait Schema Example ## Related Resources Learn how to set up the proxy for automatic injection Configure custom traits for your support use case ``` ``` # Conversational Recommender / E-Commerce Assistant Source: https://docs.getprofile.org/use-cases/ecommerce-assistant Chat-based product finders with persistent style and preference memory ## Scenario Chat-based product finder on an e-commerce site or marketplace that remembers user preferences across sessions. ## Extraction From on-site chat, searches, clicks, and purchases, GetProfile maintains: * `style_preferences[]`: "minimalist", "streetwear", "pastel colors" * `constraints`: budget, size, materials to avoid (e.g. wool) * `brand_affinities[]` and `brand_avoidances[]` * `decision_speed`: impulse buyer vs researcher This comes from conversation like "I hate wool sweaters" or "I usually spend under \$100". ## Injection For each conversation turn: * GetProfile injects a compact preference block: * "User likes minimalist, neutral colors, hates wool, typical budget under \$80, prefers sustainable brands." * It injects a few recent **preference-confirming memories** (click/purchase events) to help retrieval. The LLM then uses this as: * Additional filters in its retrieval/system prompt ("avoid wool, budget under 80"), * Guidance for how to present results ("3 options, sorted by sustainability and price"). ## Impact * Fewer irrelevant suggestions, higher conversion, and a user who feels understood without filling forms. ## Implementation ```typescript TypeScript theme={null} import OpenAI from 'openai'; const client = new OpenAI({ apiKey: process.env.GETPROFILE_API_KEY, baseURL: 'https://api.yourserver.com/v1', defaultHeaders: { 'X-GetProfile-Id': userId, 'X-Upstream-Key': process.env.OPENAI_API_KEY, }, }); // Product recommendation request const response = await client.chat.completions.create({ model: 'gpt-5', messages: [ { role: 'system', content: 'You are a helpful shopping assistant. Recommend products that match the user\'s style and budget preferences.', }, { role: 'user', content: 'I need a new winter jacket.', }, ], }); // GetProfile injects style preferences, budget constraints, and past purchases ``` ## Trait Schema Example ```json theme={null} { "style_preferences": { "type": "array", "items": { "type": "string" }, "description": "Style categories the user prefers" }, "budget_range": { "type": "object", "properties": { "min": { "type": "number" }, "max": { "type": "number" } }, "description": "Typical spending range" }, "size_preferences": { "type": "array", "items": { "type": "string" }, "description": "Preferred sizes" }, "material_avoidances": { "type": "array", "items": { "type": "string" }, "description": "Materials the user dislikes or is allergic to" }, "brand_affinities": { "type": "array", "items": { "type": "string" }, "description": "Brands the user prefers" }, "decision_speed": { "type": "string", "enum": ["impulse", "researcher", "comparison-shopper"], "description": "How quickly the user makes purchase decisions" } } ``` ## Related Resources Set up automatic preference injection for your chat interface Store purchase events and preference confirmations # Game / NPC Personalization Source: https://docs.getprofile.org/use-cases/game-npcs AI-driven NPCs with personalized dialogue and dynamic quests ## Scenario Online game with AI-driven NPC dialogue and dynamic quests that adapt to each player's style and choices. ## Extraction From gameplay events and chat with NPCs, GetProfile tracks: * `play_style`: stealthy, aggressive, completionist, explorer * `risk_tolerance`: likes high-risk high-reward vs safe paths * `story_preferences[]`: political intrigue, romance, horror, humor * `moral_alignment`: tends to spare enemies vs ruthless choices All learned from in-game decisions and occasional user text. ## Injection Before generating NPC dialog or a quest: * Game server calls the LLM through GetProfile: * It injects the player profile: * "Player is an explorer completionist who loves political intrigue, hates horror, usually plays 'good' moral options." * It injects a few **key story memories**: * important past quests, * big choices they made. ## Impact * NPCs comment on the *right* past events. * Quest offers and story beats match their style and morals. * Same system works across multiple AI features (quests, barks, codex entries) via a shared profile. ## Implementation ```typescript TypeScript theme={null} import OpenAI from 'openai'; const client = new OpenAI({ apiKey: process.env.GETPROFILE_API_KEY, baseURL: 'https://api.yourserver.com/v1', defaultHeaders: { 'X-GetProfile-Id': playerId, 'X-Upstream-Key': process.env.OPENAI_API_KEY, }, }); // NPC dialogue generation const response = await client.chat.completions.create({ model: 'gpt-5', messages: [ { role: 'system', content: 'You are an NPC in a fantasy game. Reference the player\'s past choices and match their preferred story style.', }, { role: 'user', content: 'Generate dialogue for the merchant NPC when the player approaches.', }, ], }); // GetProfile injects player's play style, story preferences, and key past events ``` ## Trait Schema Example ```json theme={null} { "play_style": { "type": "string", "enum": ["stealthy", "aggressive", "completionist", "explorer", "speedrunner"], "description": "How the player approaches gameplay" }, "risk_tolerance": { "type": "string", "enum": ["low", "medium", "high"], "description": "Player's preference for risky vs safe gameplay" }, "story_preferences": { "type": "array", "items": { "type": "string", "enum": ["political-intrigue", "romance", "horror", "humor", "drama", "mystery"] }, "description": "Story themes the player enjoys" }, "moral_alignment": { "type": "string", "enum": ["good", "neutral", "evil", "chaotic"], "description": "Player's typical moral choices" }, "quest_completion_rate": { "type": "number", "description": "Percentage of quests the player completes" } } ``` ## Related Resources Store quest completions and significant player choices Update player traits based on gameplay events # Internal Knowledge Assistant Tailored to Each Employee Source: https://docs.getprofile.org/use-cases/knowledge-assistant Company AI assistants that adapt to each employee's role and expertise ## Scenario Internal "Ask AI" for company docs/code/processes that needs to adapt to each employee's role and knowledge level. ## Extraction From questions, docs visited, and tools used, GetProfile creates: * `department`, `team`, `role` * `project_contexts[]`: which repos / services they touch * `topic_familiarity`: "deep in payments service", "novice in infra", etc. * `documentation_style_preference`: likes code samples vs concept docs All inferred from their questions and link click patterns. ## Injection When they ask, "How do I add a new metric to our billing pipeline?": * GetProfile injects: * "User is on the billing team, familiar with service X but not Y; prefers answers with code snippets and direct links to runbooks." * Plus a couple of relevant memories: * previous similar question and answer, * docs they read last time. ## Impact * The assistant answers at the right depth and with the right references. * Onboarding is smoother, since the assistant adapts to each newcomer over time. ## Implementation ```typescript TypeScript theme={null} import OpenAI from 'openai'; const client = new OpenAI({ apiKey: process.env.GETPROFILE_API_KEY, baseURL: 'https://api.yourserver.com/v1', defaultHeaders: { 'X-GetProfile-Id': employeeId, 'X-Upstream-Key': process.env.OPENAI_API_KEY, }, }); // Knowledge base query const response = await client.chat.completions.create({ model: 'gpt-5', messages: [ { role: 'system', content: 'You are an internal knowledge assistant. Provide answers at the appropriate depth for the employee\'s role and expertise.', }, { role: 'user', content: 'How do I add a new metric to our billing pipeline?', }, ], }); // GetProfile injects employee's role, expertise areas, and documentation preferences ``` ## Trait Schema Example ```json theme={null} { "department": { "type": "string", "description": "Employee's department" }, "team": { "type": "string", "description": "Employee's team" }, "role": { "type": "string", "description": "Job title or role" }, "project_contexts": { "type": "array", "items": { "type": "string" }, "description": "Repositories, services, or projects the employee works on" }, "topic_familiarity": { "type": "object", "description": "Familiarity level per topic or service", "additionalProperties": { "type": "string", "enum": ["novice", "intermediate", "expert"] } }, "documentation_style_preference": { "type": "string", "enum": ["code-samples", "concept-docs", "step-by-step", "reference"], "description": "Preferred documentation format" } } ``` ## Related Resources Set up automatic context injection for your knowledge base Store and retrieve past questions and answers # Multi-Surface Personal Assistant (Telegram, Web, Desktop) Source: https://docs.getprofile.org/use-cases/multi-surface-assistant One personal AI that works consistently across all platforms ## Scenario One "personal AI" used across chat, browser extension, and desktop app that needs to maintain consistent context across all surfaces. ## Extraction From all surfaces, GetProfile builds a single user profile: * `life_domains[]`: work, fitness, finances, hobby projects * `time_constraints`: works late, free on weekends, calls best in evenings * `tool_preferences[]`: Notion, Google Calendar, Todoist, VSCode * `planning_style`: likes detailed plans vs loose suggestions * `privacy_boundaries`: topics user asked not to store or revisit It learns these passively as the user chats and issues commands. ## Injection Any time any surface calls an LLM: * GetProfile's proxy injects: * "User is a freelance dev, uses Notion & Google Calendar, prefers weekly overview plans, often overwhelmed by too many micro tasks." * It chooses a few cross-surface memories: * last week's goals, * tasks the user procrastinated on, * commitments noted in other channels. ## Impact * The Telegram bot, desktop agent, and browser extension all "share a brain". * The assistant proposes realistic plans and reminders consistent with how the user actually behaves, not just generic advice. ## Implementation ```typescript TypeScript theme={null} // Telegram Bot import OpenAI from 'openai'; const telegramClient = new OpenAI({ apiKey: process.env.GETPROFILE_API_KEY, baseURL: 'https://api.yourserver.com/v1', defaultHeaders: { 'X-GetProfile-Id': userId, 'X-Upstream-Key': process.env.OPENAI_API_KEY, }, }); // Browser Extension const extensionClient = new OpenAI({ apiKey: process.env.GETPROFILE_API_KEY, baseURL: 'https://api.yourserver.com/v1', defaultHeaders: { 'X-GetProfile-Id': userId, // Same user ID 'X-Upstream-Key': process.env.OPENAI_API_KEY, }, }); // Desktop App const desktopClient = new OpenAI({ apiKey: process.env.GETPROFILE_API_KEY, baseURL: 'https://api.yourserver.com/v1', defaultHeaders: { 'X-GetProfile-Id': userId, // Same user ID 'X-Upstream-Key': process.env.OPENAI_API_KEY, }, }); // All three clients share the same profile and memories ``` ## Trait Schema Example ```json theme={null} { "type": "object", "properties": { "life_domains": { "type": "array", "items": { "type": "string" }, "description": "Areas of life the user manages (work, fitness, finances, etc.)" }, "time_constraints": { "type": "object", "properties": { "work_schedule": { "type": "string" }, "free_times": { "type": "array", "items": { "type": "string" } }, "preferred_call_times": { "type": "array", "items": { "type": "string" } } }, "description": "User's availability and time preferences" }, "tool_preferences": { "type": "array", "items": { "type": "string" }, "description": "Tools and apps the user prefers to use" }, "planning_style": { "type": "string", "enum": ["detailed", "loose", "minimal"], "description": "How much structure the user prefers in plans" }, "privacy_boundaries": { "type": "array", "items": { "type": "string" }, "description": "Topics the user has asked not to be stored or referenced" } } } ``` ## Related Resources Integrate GetProfile into multiple platforms with the JavaScript SDK Access user profiles from any surface # Use Cases Source: https://docs.getprofile.org/use-cases/overview Real-world applications of GetProfile ## Overview GetProfile enables AI applications to maintain persistent user profiles and long-term memory across conversations. Here are real-world use cases that demonstrate how different industries and applications can benefit from GetProfile's extraction and injection capabilities. Support chatbots that remember user context and preferences Learning companions that adapt to each student's needs CRM assistants with account and contact intelligence Conversational product recommenders with style memory Personal AI that works across Telegram, web, and desktop Internal AI assistants tailored to each employee AI-driven NPCs with personalized dialogue and quests Extraction-first service for analytics and routing ## Common Patterns All these use cases follow GetProfile's core pattern: GetProfile automatically extracts structured traits and memories from conversations, events, and interactions. Profiles are maintained continuously, updating as new information becomes available. Relevant context is automatically injected into LLM prompts via the proxy or SDK. AI interactions become personalized, contextual, and more effective. ## Getting Started Ready to implement one of these use cases? Start with the [Quickstart](/quickstart) guide, then explore the [Integration Options](/integrations/overview) to choose the right approach for your application. # Profile Miner as a Service (Extraction-First, Injection-Optional) Source: https://docs.getprofile.org/use-cases/profile-mining Extraction-first service for analytics, routing, and other internal logic ## Scenario Team wants structured user profiles for analytics, routing, or other internal logic — not just for LLM prompts. ## Extraction They stream conversation logs, events, and reviews to GetProfile: * It extracts traits like: * `NPS_risk`: likely detractor vs promoter * `churn_risk_reasons[]` * `product_feature_needs[]` * `expertise` / `segment` (SMB vs enterprise, hobbyist vs pro) ## Injection * Some teams might still have their own LLM stack; they just call `GET /profiles/:id` to build prompts manually. * Others may later switch to GetProfile proxy to get **automatic injection** with the same traits. ## Impact * They can use traits **everywhere**: * marketing segmentation, * feature flags ("show advanced UI for experts"), * routing to different flows based on `segment` or `risk`. * And, if/when they want, they get "free" prompt injection by pointing their LLM client to the GetProfile proxy. ## Implementation ```typescript TypeScript theme={null} // Option 1: Extract-only (using SDK) import { GetProfileClient } from '@getprofile/sdk'; const client = new GetProfileClient({ apiKey: process.env.GETPROFILE_API_KEY, baseURL: 'https://api.yourserver.com', }); // Stream events to extract traits await client.memories.create({ profileId: userId, content: 'User canceled subscription, mentioned pricing concerns', metadata: { event_type: 'churn_event', }, }); // Later, retrieve profile for analytics const profile = await client.profiles.get(userId); const traits = await client.traits.list(userId); // Use traits for routing, feature flags, etc. if (traits.find(t => t.name === 'segment' && t.value === 'enterprise')) { // Show enterprise features } // Option 2: Extract + Inject (using proxy) import OpenAI from 'openai'; const llmClient = new OpenAI({ apiKey: process.env.GETPROFILE_API_KEY, baseURL: 'https://api.yourserver.com/v1', defaultHeaders: { 'X-GetProfile-Id': userId, 'X-Upstream-Key': process.env.OPENAI_API_KEY, }, }); // LLM calls automatically get profile injection const response = await llmClient.chat.completions.create({ model: 'gpt-5', messages: [{ role: 'user', content: 'Help me with...' }], }); ``` ## Trait Schema Example ```json theme={null} { "NPS_risk": { "type": "string", "enum": ["promoter", "passive", "detractor"], "description": "Likely Net Promoter Score category" }, "churn_risk": { "type": "string", "enum": ["low", "medium", "high", "critical"], "description": "Risk level of user churning" }, "churn_risk_reasons": { "type": "array", "items": { "type": "string" }, "description": "Factors contributing to churn risk" }, "product_feature_needs": { "type": "array", "items": { "type": "string" }, "description": "Features the user has expressed interest in or needs" }, "expertise": { "type": "string", "enum": ["beginner", "intermediate", "advanced", "expert"], "description": "User's expertise level" }, "segment": { "type": "string", "enum": ["hobbyist", "smb", "mid-market", "enterprise"], "description": "User segment classification" } } ``` ## Use Cases for Extracted Profiles Route users to different marketing campaigns based on extracted traits Show/hide features based on user expertise or segment Route support tickets to appropriate teams based on user profile Build dashboards and reports from structured profile data ## Related Resources Programmatic access to profiles and traits Retrieve profiles for analytics and routing Export profile data for external analytics tools Add automatic injection when ready # Sales / Success Copilot With Account & Contact Profiles Source: https://docs.getprofile.org/use-cases/sales-copilot AI copilots in CRM tools with intelligent account and contact context ## Scenario AI copilot inside a CRM / sales engagement tool that needs to remember account and contact details across interactions. ## Extraction From emails, call notes, support tickets, and CRM events, GetProfile extracts traits for: ### Contact-level traits: * `role`, `seniority`, `decision_power` * `communication_style`: short & blunt vs narrative * `topics_that_resonate[]`: ROI, compliance, integrations * `objections[]`: price, migration risk, security ### Account-level traits: * `company_size`, `industry` * `tech_stack[]` * `deal_stage`, `champions[]`, `blockers[]` No salesperson tags this by hand; it's distilled from interaction logs. ## Injection When a rep asks: > "Draft a follow-up email to Sarah about the pilot." The copilot's LLM call goes through GetProfile: * GetProfile injects contact + account profile: * "Sarah: VP Eng, high decision power, cares about migration risk and reliability; previously expressed worry about vendor lock-in." * It injects a few relevant memories: * last meeting summary, * her exact wording on concerns. ## Impact The copilot: * Frames messages around the right value props, * Remembers objections over weeks/months, * Tailors tone and detail to the specific contact. ## Implementation ```typescript TypeScript theme={null} import OpenAI from 'openai'; const client = new OpenAI({ apiKey: process.env.GETPROFILE_API_KEY, baseURL: 'https://api.yourserver.com/v1', defaultHeaders: { 'X-GetProfile-Id': `${accountId}:${contactId}`, // Composite ID 'X-Upstream-Key': process.env.OPENAI_API_KEY, }, }); // Sales copilot request const response = await client.chat.completions.create({ model: 'gpt-5', messages: [ { role: 'system', content: 'You are a sales assistant. Draft emails that address the contact\'s specific concerns and preferences.', }, { role: 'user', content: 'Draft a follow-up email to Sarah about the pilot program.', }, ], }); // GetProfile injects Sarah's profile, account context, and past interactions ``` ## Trait Schema Example ```json theme={null} { "role": { "type": "string", "description": "Contact's job role" }, "seniority": { "type": "string", "enum": ["junior", "mid", "senior", "executive"], "description": "Seniority level" }, "decision_power": { "type": "string", "enum": ["low", "medium", "high", "decision-maker"], "description": "Influence on purchasing decisions" }, "communication_style": { "type": "string", "enum": ["concise", "detailed", "narrative"], "description": "Preferred communication approach" }, "topics_that_resonate": { "type": "array", "items": { "type": "string" }, "description": "Topics that generate positive responses" }, "objections": { "type": "array", "items": { "type": "string" }, "description": "Common concerns or objections raised" }, "company_size": { "type": "string", "enum": ["startup", "smb", "mid-market", "enterprise"], "description": "Size of the account company" }, "industry": { "type": "string", "description": "Primary industry of the account" }, "tech_stack": { "type": "array", "items": { "type": "string" }, "description": "Key technologies or tools the account uses" }, "deal_stage": { "type": "string", "enum": ["prospecting", "qualification", "proposal", "negotiation", "closed"], "description": "Current stage in the sales process" }, "champions": { "type": "array", "items": { "type": "string" }, "description": "Account contacts advocating for the deal" }, "blockers": { "type": "array", "items": { "type": "string" }, "description": "Account contacts slowing or blocking the deal" } } ``` ## Related Resources Retrieve contact and account profiles programmatically Build custom sales workflows with the JavaScript SDK