# 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