# POST /completions
Source: https://docs.curvelabs.org/api-reference/completions
Use ThinkFeel through an OpenAI-compatible text completions endpoint.
ThinkFeel supports an OpenAI-compatible text completions endpoint at `/api/v1/completions`. OpenAI SDKs for Completions, including AI SDK, can point their base URL to `https://playground.curvelabs.org/api/v1`.
```text theme={null}
POST https://playground.curvelabs.org/api/v1/completions
```
This is different from the ThinkFeel SDK and CLI base URL setting, which should use only the site origin. Send either
`prompt` or `messages`, not both. `model` must be your persona ID.
## Headers
| Header | Type | Required | Description |
| --------------- | ------ | ----------- | ------------------------------------------------------------------------------ |
| `Content-Type` | string | Yes | Must be `application/json` |
| `Authorization` | string | Conditional | OpenAI-style bearer API key for API-key billing |
| `x-api-key` | string | Conditional | Alternative to `Authorization`; one key header is required for API-key billing |
## Body parameters
| Parameter | Type | Required | Description |
| ---------- | ---------------------- | ----------- | ----------------------------------------------------------------------------------------------------------------------------- |
| `model` | string | Yes | Persona ID UUID only |
| `messages` | array | Conditional | Multi-turn text messages; accepts `user`, `assistant`, `system`, and `developer` roles with string or text-part array content |
| `prompt` | string or string array | Conditional | Non-empty text prompt, as a string or one-item string array, when `messages` is omitted |
| `n` | number | No | Number of completions, 1-5. Defaults to 1 |
| `echo` | boolean | No | Prepend the prompt to the output text |
| `stop` | string or string array | No | Stop sequence or sequences |
## Compatibility notes
* Provide either `prompt` or `messages`.
* When using `messages`, include the full conversation history, put `system` and `developer` context before the final
user turn, and send non-empty content in that final user message.
* `messages` supports `user`, `assistant`, `system`, and `developer` roles.
* `model` must be your persona ID UUID provided during onboarding.
* `prompt` must be a non-empty string or a single-element string array.
* `n` is capped at 5.
* Message content must be a plain string or an array of text parts.
* Optional `timestamp`, `createdAt`, and `created_at` values are accepted and normalized when present.
* Streaming is not supported on this V1 endpoint. `stream: true` returns an error.
* `max_tokens` and `logprobs` are rejected.
* Other OpenAI parameters are accepted but ignored: `temperature`, `top_p`, `presence_penalty`, `frequency_penalty`, `best_of`, `seed`, `suffix`, and `user`.
* Output text is normalized, stop sequences are applied, and `echo` prepends the prompt.
* Responses omit `usage`.
## Request
```bash theme={null}
curl -X POST https://playground.curvelabs.org/api/v1/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY_HERE" \
-d '{
"model": "YOUR_PERSONA_ID",
"prompt": "hey, how's your day?",
"n": 1,
"echo": false,
"stop": ["\n\n"]
}'
```
## Response
```json theme={null}
{
"id": "cmpl-...",
"object": "text_completion",
"created": 1737656583,
"model": "YOUR_PERSONA_ID",
"choices": [
{
"text": "pretty chill so far, you?",
"index": 0,
"logprobs": null,
"finish_reason": "stop",
"chunks": ["pretty chill so far, you?"]
}
]
}
```
# POST /generate
Source: https://docs.curvelabs.org/api-reference/generate
Generate an AI response based on conversation history.
Use `/generate` for direct ThinkFeel requests. Send a persona ID and full message history. The response returns the selected final reply and optional alternatives.
```text theme={null}
POST https://playground.curvelabs.org/api/v1/generate
```
## Headers
| Header | Type | Required | Description |
| --------------- | ------ | ----------- | ----------------------------------------------------- |
| `Content-Type` | string | Yes | Must be `application/json` |
| `x-api-key` | string | Conditional | API key for API-key billing |
| `Authorization` | string | Conditional | Alternative bearer format: `Bearer YOUR_API_KEY_HERE` |
## Body parameters
| Parameter | Type | Required | Description |
| ---------------------- | -------------------- | -------- | -------------------------------------------------------------------------------- |
| `personaId` | string | Yes | Persona UUID provided during onboarding |
| `messages` | array | Yes | Conversation messages to use as context |
| `messages[].role` | string | Yes | `user`, `assistant`, `system`, or `developer` |
| `messages[].content` | string or text array | Yes | Plain text, or an array of text parts shaped as `{ type: "text", text: string }` |
| `messages[].timestamp` | number or string | No | Optional timestamp; `createdAt` and `created_at` are also accepted |
| `includeVariations` | boolean | No | Return alternative reply choices. Defaults to `false` |
`messages[]` must contain at least one message. Use `user` for the final user turn; `assistant` cannot be the last message.
`system` and `developer` messages are accepted as text-only context and should be placed before the final user turn. The
final user message must have non-empty content. Only text content is supported.
## Request
```bash theme={null}
curl -X POST https://playground.curvelabs.org/api/v1/generate \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY_HERE" \
-d '{
"personaId": "YOUR_PERSONA_ID",
"messages": [
{
"role": "user",
"content": "Hello!"
}
]
}'
```
## Default response
When `includeVariations` is `false`, the API returns the final reply and chunks.
```json theme={null}
{
"status": "success",
"result": {
"finalReply": "hey! what's up?",
"chunks": ["hey! what's up?"]
}
}
```
## Response with variations
When `includeVariations` is `true`, the API may include `replyChoices`.
```json theme={null}
{
"status": "success",
"result": {
"finalReply": "hey! what's up?",
"chunks": ["hey! what's up?"],
"replyChoices": ["hey! what's up?", "hi there! how's it going?", "hey! good to hear from you"]
}
}
```
API-key `/generate` responses may also include a top-level `rateLimits` array when rate-limit metadata is available for your account.
## SDK and CLI notes
The ThinkFeel SDK wraps `/api/v1/generate` and `/api/v1/personify`. It does not wrap `/api/v1/completions`, which remains
the OpenAI-compatible endpoint.
The ThinkFeel SDK package is currently `0.1.6` and supports:
* `new ThinkFeel({ apiKey, personaId, baseUrl? })`
* `generate({ messages, includeVariations? })`
* `personify({ raw })`
When setting SDK `baseUrl`, pass only the site origin, such as `https://playground.curvelabs.org`; the SDK appends `/api/v1` internally.
# POST /personify
Source: https://docs.curvelabs.org/api-reference/personify
Rewrite source text into a persona voice enabled for your account.
Use `/personify` when you already have source text and want to rewrite it in a persona voice enabled for your account.
```text theme={null}
POST https://playground.curvelabs.org/api/v1/personify
```
## Headers
| Header | Type | Required | Description |
| --------------- | ------ | ----------- | ---------------------------------- |
| `Content-Type` | string | Yes | Must be `application/json` |
| `Authorization` | string | Conditional | Bearer API key for API-key billing |
| `x-api-key` | string | Conditional | Alternative to `Authorization` |
## Body parameters
| Parameter | Type | Required | Description |
| ----------- | ------ | -------- | ------------------------------------------------------------- |
| `personaId` | string | Yes | Persona UUID with `/personify` access |
| `raw` | string | Yes | Non-empty source text to rewrite; not treated as instructions |
## Request
```bash theme={null}
curl -X POST https://playground.curvelabs.org/api/v1/personify \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY_HERE" \
-d '{
"personaId": "YOUR_PERSONIFY_PERSONA_ID",
"raw": "Thanks for reaching out. We can help answer customer questions faster while keeping the tone warm and clear."
}'
```
## Response
```json theme={null}
{
"personified": "thanks for reaching out. we can help answer customer questions faster while keeping the tone warm and clear.",
"chunks": [
"thanks for reaching out.",
"we can help answer customer questions faster while keeping the tone warm and clear."
]
}
```
# Authentication
Source: https://docs.curvelabs.org/authentication
Authenticate ThinkFeel SDK, CLI, and OpenAI-compatible requests.
Send one API key header with each request. ThinkFeel accepts either the `x-api-key` header or an OpenAI-style bearer token.
Generate keys yourself after onboarding is approved. API-key billing is the standard path, so send one key header on every request. Payment challenges only appear on allowlisted self-serve endpoints or personas when Curve Labs enables that option for your account.
## Supported API key headers
```text theme={null}
x-api-key: YOUR_API_KEY_HERE
Authorization: Bearer YOUR_API_KEY_HERE
```
For OpenAI-compatible clients, send your key as `Authorization: Bearer`. The `x-api-key` header is still supported for direct ThinkFeel requests.
## Environment variables
SDK and CLI projects should use:
| Variable | Required | Notes |
| ---------------------- | -------- | ---------------------------------------------------------------------------- |
| `THINKFEEL_API_KEY` | Yes | API key created in Playground, through CLI login, or by the ThinkFeel Plugin |
| `THINKFEEL_PERSONA_ID` | Yes | Persona UUID provided during onboarding |
| `THINKFEEL_BASE_URL` | No | Origin only, such as `https://playground.curvelabs.org` |
OpenAI-compatible clients should point their client base URL to `https://playground.curvelabs.org/api/v1`. Depending on the client library, you may need to store the same key as `OPENAI_API_KEY`.
## CLI login and profiles
```bash theme={null}
npx thinkfeel login
npx thinkfeel login --persona-id YOUR_THINKFEEL_PERSONA_ID
npx thinkfeel login --name "Local dev key" --persona-id YOUR_THINKFEEL_PERSONA_ID
npx thinkfeel login --profile work --persona-id YOUR_THINKFEEL_PERSONA_ID
npx thinkfeel profiles
npx thinkfeel use work
```
`thinkfeel login` is the preferred way to set up keys for approved Playground users. It opens browser sign-in, creates a Playground API key after confirmation, encrypts the key to a local callback, and saves it in the ThinkFeel CLI profile without printing the unencrypted key.
## Agent-assisted setup
Codex and Claude Code users can install the ThinkFeel Plugin from [CLI + Agents](/cli-agents). The plugin does not run `npx thinkfeel login`; it uses its own local helper with the same Playground browser approval flow, confirms the local env-file destination, then writes `THINKFEEL_API_KEY` and optional `THINKFEEL_PERSONA_ID` without exposing plaintext in chat.
## Non-interactive configuration
```bash theme={null}
printf '%s\n' "$THINKFEEL_API_KEY" | npx thinkfeel configure --api-key-stdin --persona-id YOUR_THINKFEEL_PERSONA_ID
npx thinkfeel configure --api-key-env THINKFEEL_API_KEY --persona-id YOUR_THINKFEEL_PERSONA_ID
npx thinkfeel configure --profile work --api-key-env WORK_THINKFEEL_API_KEY --persona-id YOUR_THINKFEEL_PERSONA_ID
npx thinkfeel configure --show
```
Use `--api-key-env` or `--api-key-stdin` for automation. Passing API keys as command arguments is intentionally unsupported because shell history and process lists can expose them.
Never expose your API key in client-side or public code. Store API keys as environment variables or managed secrets, rotate
keys periodically, and use HTTPS for all requests.
# CLI and agents
Source: https://docs.curvelabs.org/cli-agents
Use the ThinkFeel package for local development, coding agents, and safe key setup.
The npm package includes the TypeScript SDK, the `thinkfeel` CLI, and a `SKILL.md` file that tells coding agents how to use ThinkFeel safely. The ThinkFeel Plugin adds a separate Codex and Claude Code setup flow for agent-managed local env files.
The plugin does not call `npx thinkfeel login`. It uses a local plugin helper, asks you to approve an env-file destination, opens the same Playground browser approval flow, and writes `THINKFEEL_API_KEY` to that env file. Use `thinkfeel login` when you want a standalone CLI profile.
## Install for agents
Codex users should add the ThinkFeel marketplace and install the plugin:
```bash theme={null}
codex plugin marketplace add artificial-affect/thinkfeel-plugin
codex plugin add thinkfeel-plugin@thinkfeel
```
Start a new Codex thread after installation so Codex loads the ThinkFeel plugin skill and MCP server.
Claude Code users can install the same plugin from the ThinkFeel marketplace:
```bash theme={null}
/plugin marketplace add artificial-affect/thinkfeel-plugin
/plugin install thinkfeel-plugin@thinkfeel
```
Run `/reload-plugins` or start a new Claude Code session after installation.
## Install the package or run one-off
```bash theme={null}
npm i @curvelabs.org/thinkfeel
# One-off usage without adding the package to a project
npx --package @curvelabs.org/thinkfeel thinkfeel --help
```
## Login, name keys, and switch profiles
```bash theme={null}
npx thinkfeel login
npx thinkfeel login --persona-id YOUR_THINKFEEL_PERSONA_ID
npx thinkfeel login --name "Local dev key" --persona-id YOUR_THINKFEEL_PERSONA_ID
npx thinkfeel login --profile work --persona-id YOUR_THINKFEEL_PERSONA_ID
npx thinkfeel profiles
npx thinkfeel use work
```
## Configure CI or local scripts
```bash theme={null}
printf '%s\n' "$THINKFEEL_API_KEY" | npx thinkfeel configure --api-key-stdin --persona-id YOUR_THINKFEEL_PERSONA_ID
npx thinkfeel configure --api-key-env THINKFEEL_API_KEY --persona-id YOUR_THINKFEEL_PERSONA_ID
npx thinkfeel configure --profile work --api-key-env WORK_THINKFEEL_API_KEY --persona-id YOUR_THINKFEEL_PERSONA_ID
npx thinkfeel configure --show
```
## Generate, personify, inspect JSON, and clear config
```bash theme={null}
npx thinkfeel generate "Can we talk later?"
npx thinkfeel generate "Can we talk later?" --variations
npx thinkfeel generate "Can we talk later?" --json
npx thinkfeel personify "Thanks for reaching out. Send me the details when you have them."
npx thinkfeel configure --clear --profile work
```
## Inspect the package skill
```bash theme={null}
cat node_modules/@curvelabs.org/thinkfeel/SKILL.md
```
```markdown theme={null}
---
name: thinkfeel-sdk
description: Use when a coding agent needs to install, configure, or call the ThinkFeel SDK or CLI for generate/personify work.
---
# ThinkFeel SDK
Use this skill when a project needs ThinkFeel API access through @curvelabs.org/thinkfeel or the thinkfeel CLI.
Rules:
- Never ask the user to paste an API key into chat.
- If the ThinkFeel Plugin is installed in Codex or Claude Code, prefer its Playground API-key credential gate for project env-file setup.
- Prefer npx thinkfeel login for standalone CLI profiles.
- Use the persona ID the user already has; do not invent one.
- Verify setup with npx thinkfeel configure --show without exposing secrets.
```
## Browser login URL details
```text theme={null}
GET /api/thinkfeel/cli/login?state=...&name=...&source=cli&redirect_uri=http://127.0.0.1:PORT/thinkfeel/callback&recipient_public_key_jwk=...
Required query params:
- state: random value generated by the caller to protect the login session
- redirect_uri: http loopback URL only (127.0.0.1, localhost, or ::1)
- recipient_public_key_jwk: base64url-encoded RSA public JWK
Optional query params:
- name: display name for the generated API key
- source: cli, codex, or claude_code
Do not use this route as a general key-creation API. The browser-authenticated confirmation page returns only an encrypted `encrypted_api_key` value to the local callback.
```
Automated setup should use the ThinkFeel Plugin in Codex or Claude Code, or `npx thinkfeel login` for CLI profiles. Do not
ask users to paste API keys into chat. If browser login is unavailable, use `configure --api-key-env` or `configure --api-key-stdin`.
# Custom personas
Source: https://docs.curvelabs.org/concepts/personas
Design custom ThinkFeel personas for your product and audience.
Instead of asking you to tune a generic model yourself, Curve Labs works with you to create a persona for your product. During onboarding, the team helps define the personality, audience fit, and boundaries.
## Personality traits
* **Extraversion vs. introversion:** energetic and outgoing vs. thoughtful and reserved
* **Emotional tone:** warm, professional, playful, or another use-case-specific tone
* **Confidence level:** bold and assertive vs. humble
* **Humor style:** dry wit, playful banter, or no humor
## Communication style
* **Formality:** casual slang vs. professional language
* **Verbosity:** concise replies vs. detailed explanations
* **Emotional expression:** expressive vs. reserved
* **Question frequency:** inquisitive vs. statement-focused
## Behavioral guidelines
* **Role definition:** friend, peer supporter, coach, assistant, or another clear role
* **Boundaries:** topics to avoid or handle sensitively
* **Engagement style:** proactive vs. reactive
* **Cultural context:** regional slang and cultural references
## Match personas to use cases
* **Customer support:** patient and solution-focused
* **Mental wellness:** non-judgmental and validating
* **Entertainment:** engaging, playful, and creative
* **Education:** encouraging and adaptive
During onboarding, you receive a unique `personaId` in UUID format. Use that ID in all API requests. It is tied to your
custom persona configuration and can be refined over time based on feedback and usage.
# Cookbook API request examples
Source: https://docs.curvelabs.org/cookbooks/api-request-examples
Request and response examples for the cookbook quickstart, chat, and personify routes.
## `POST /api/quickstart`
### Request
```json theme={null}
{
"prompt": "hello",
"personaId": "optional-uuid"
}
```
### Response
```json theme={null}
{
"text": "...",
"model": "persona-uuid"
}
```
## `POST /api/chat`
### Request
```json theme={null}
{
"messages": [{ "role": "user", "content": "hello" }],
"personaId": "optional-uuid"
}
```
### Response
```json theme={null}
{
"reply": "...",
"model": "persona-uuid"
}
```
## `POST /api/personify`
### Request
```json theme={null}
{
"raw": "Base response to rewrite",
"personaId": "optional-uuid"
}
```
### Response
```json theme={null}
{
"personified": "...",
"chunks": ["..."],
"model": "persona-uuid"
}
```
## Shared error response
```json theme={null}
{
"error": {
"type": "validation | config | provider",
"message": "..."
}
}
```
# Cookbooks
Source: https://docs.curvelabs.org/cookbooks/index
Practical build guides for ThinkFeel integrations.
This section summarizes the current `curve-labs-cookbooks` examples. Use these guides for quick starts, then review the production checklist before launching.
## Repository snapshot
| Field | Value |
| ------------- | ----------------------------- |
| Organization | `artificial-affect` |
| Repository | `curve-labs-cookbooks` |
| Latest commit | `f141cac` |
| Commit date | July 6, 2026 |
| Summary | signpost thinkfeel agent docs |
```bash theme={null}
git clone https://github.com/artificial-affect/curve-labs-cookbooks.git
cd curve-labs-cookbooks
git log -1 --oneline
```
## Available cookbooks
Plain Node.js scripts for checking SDK configuration with environment variables.
Accessible quickstart, multi-turn chat, and personify routes using ThinkFeel's non-stream responses.
## Reference pages
Request and response examples for the cookbook quickstart, chat, and personify routes.
Required controls before launching a cookbook example.
Scripts and Chat SDK adaptation notes.
# Local SDK check
Source: https://docs.curvelabs.org/cookbooks/local-sdk-check
Verify ThinkFeel SDK setup from a minimal Node.js project.
Use this recipe when you want to verify local SDK setup without a UI.
## Environment variables
```bash theme={null}
export THINKFEEL_API_KEY=your_thinkfeel_api_key
export THINKFEEL_PERSONA_ID=your_persona_uuid
# Optional
export CURVE_PROMPT="hey what's up?"
export THINKFEEL_BASE_URL="https://playground.curvelabs.org/api/v1"
```
## Install and run
```bash theme={null}
npm install
npm start
# Offline tests
npm test
```
This local SDK check is for configuration testing only. It is not a deployed service and does not include user
authentication, tenancy isolation, or abuse controls by default.
## Base URL behavior
The cookbook examples use an OpenAI-compatible provider base URL, so `THINKFEEL_BASE_URL` defaults to `https://playground.curvelabs.org/api/v1`.
ThinkFeel SDK and CLI setup are different. Pass only the site origin, such as `https://playground.curvelabs.org`, when configuring `ThinkFeel` directly.
## Auth setup
New cookbook setup should use `THINKFEEL_API_KEY` and `THINKFEEL_PERSONA_ID`.
The repo still reads legacy `CURVE_API_KEY` and `CURVE_PERSONA_ID` only when the matching `THINKFEEL_*` value is absent.
Prefer `npx thinkfeel login` or `npx thinkfeel configure --api-key-env THINKFEEL_API_KEY` to avoid pasting keys into commands.
# Next.js app cookbook
Source: https://docs.curvelabs.org/cookbooks/nextjs-chat
Run a full-stack ThinkFeel quickstart, multi-turn chat, and personify example.
This cookbook includes quickstart, multi-turn chat, and personify pages. Quickstart and chat use the OpenAI-compatible provider base URL; personify calls the native JSON endpoint.
## Local setup
```bash theme={null}
cd nextjs-example
cp env.example .env.local
npm install
npm run dev
```
## `.env.local`
```bash theme={null}
THINKFEEL_API_KEY=your_thinkfeel_api_key
THINKFEEL_PERSONA_ID=your_persona_uuid
# Optional
THINKFEEL_BASE_URL=https://playground.curvelabs.org/api/v1
```
## Included routes
| Route | Purpose |
| ---------------------- | ------------------------ |
| `GET /` | Quickstart UI |
| `GET /chat` | Multi-turn chat UI |
| `GET /personify` | Native personify UI |
| `POST /api/quickstart` | Completion sample |
| `POST /api/chat` | Non-stream chat exchange |
| `POST /api/personify` | Native personify rewrite |
## Key notes
* Uses Vercel AI SDK provider setup with ThinkFeel's OpenAI-compatible base URL.
* ThinkFeel completions are non-stream. The UI expects complete reply responses.
* ThinkFeel personify uses the native `/api/v1/personify` JSON endpoint.
* Tool-calling and some OpenAI parameters remain unsupported.
* Error responses are grouped as validation, config, or provider errors.
* If overriding `THINKFEEL_BASE_URL` for this OpenAI-compatible cookbook, include `/api/v1`.
* For ThinkFeel SDK or CLI setup, use the origin only.
* Legacy `CURVE_*` environment names remain fallback-only.
* New setup should use `THINKFEEL_*` names.
# Production checklist
Source: https://docs.curvelabs.org/cookbooks/production-checklist
Controls required before launching cookbook examples.
Both cookbook READMEs state that these examples are starting points, not production-ready defaults.
Do not expose cookbook routes publicly until you have added product-specific auth, abuse controls, monitoring, and
data-handling policies.
## Required before launch
* Authenticate and authorize every API route with user and tenant checks.
* Apply edge and app rate limiting, throttling, and abuse controls.
* Review CORS and CSRF policies.
* Enforce strict request limits.
* Set up structured logs, error reporting, and alerting for provider failures.
* Store and rotate API keys in managed secret stores.
* Enforce retention, deletion, and sensitive-data sanitization policies.
* Define reliability and cost safeguards, including budgets, retries, and circuit breakers.
# Testing and migration
Source: https://docs.curvelabs.org/cookbooks/testing-migration
Run cookbook checks and adapt chat SDK apps for ThinkFeel.
Use the shipped scripts first, then move to live tests only after environment configuration is confirmed.
## Next.js cookbook scripts
```bash theme={null}
cd nextjs-example
npm run dev
npm run test
npm run typecheck
npm run build
# Optional live smoke test
npm run test:live
```
## Chat SDK changes
* Replace streaming responses with non-stream completion calls for ThinkFeel.
* Use persona UUID as the completion model identifier.
* Send full multi-turn message history on each request.
* Do not assume token streaming in client transport or UI state.
# FAQ
Source: https://docs.curvelabs.org/faq
Answers to common ThinkFeel API questions.
Beta access uses pricing close to the cost of the underlying models. This keeps beta usage fair. Curve Labs may introduce
a free tier in the future based on community feedback.
During your onboarding call, Curve Labs works with you to design a custom persona tailored to your use case. The team
collaboratively defines personality traits, communication style, and behavioral guidelines that match your product needs.
No. The API is stateless for conversation continuity. Send the relevant message history with each request and manage
long-term conversation state on your end.
For most production use cases, the default response is sufficient. Enable `includeVariations: true` when you want to show
users alternative response options or inspect other replies the AI generated.
Yes. Curve Labs encourages ongoing refinement. Contact support to adjust your persona based on real-world usage and
feedback.
You do not choose a model directly. Use your assigned persona ID as `personaId`, or as `model` on the OpenAI-compatible
endpoint. Curve Labs keeps the request format stable as backend models evolve.
Yes. Contact Curve Labs through [support](https://curvelabs.org/support) with your use case and expected volume. Higher
limits are available for production-level projects.
# Impact studies
Source: https://docs.curvelabs.org/impact-studies/index
Customer examples built with ThinkFeel.
Use these studies to see which ThinkFeel endpoint fits each customer workflow.
See how Unreel.ai uses `/completions` through OpenAI-compatible SDKs.
See how Unbrowse.ai uses `/personify` with the Aiko agent.
# Agent workflows: Unbrowse.ai
Source: https://docs.curvelabs.org/impact-studies/unbrowse
How Unbrowse.ai uses ThinkFeel to rewrite agent output in a persona voice.
[Unbrowse.ai](https://unbrowse.ai/) turns website actions into tools that agents can call.
It uses [`/personify`](/api-reference/personify) with the Aiko agent. The product can send the agent's draft output to ThinkFeel, then return the rewritten result in Aiko's persona voice before the user sees it.
Use `/personify` when you already have source text and need to convert it into a specific persona voice.
# AI video content: Unreel.ai
Source: https://docs.curvelabs.org/impact-studies/unreel
How Unreel.ai uses ThinkFeel for AI video content workflows.
[Unreel.ai](https://unreel.ai/) helps creators produce AI video content for brand campaigns.
It uses [`/completions`](/api-reference/completions) through OpenAI-compatible SDKs. Existing generation code can point at ThinkFeel by setting the SDK base URL to `https://playground.curvelabs.org/api/v1` and using a ThinkFeel persona ID as the `model`.
Use `/completions` when your app already expects an OpenAI-style text completion response.
# ThinkFeel Emotion AI API
Source: https://docs.curvelabs.org/index
Build emotionally intelligent AI replies with ThinkFeel, the Curve Labs API for custom personas, chat workflows, and OpenAI-compatible apps.
Use ThinkFeel to generate replies for a custom persona. Send the messages you want the persona to consider, and receive a response your product can show or adapt.
AI that understands context, tone, and emotional subtext.
Work with Curve Labs to craft a persona tailored to your use case.
Keep full control over conversation history and context.
Pay pricing close to model costs during the early access beta.
## Install the tools
```bash theme={null}
codex plugin marketplace add artificial-affect/thinkfeel-plugin
codex plugin add thinkfeel-plugin@thinkfeel
```
Codex users should add the ThinkFeel marketplace and install the plugin. Start a new Codex thread after installation so Codex loads the ThinkFeel plugin skill and MCP server.
```bash theme={null}
/plugin marketplace add artificial-affect/thinkfeel-plugin
/plugin install thinkfeel-plugin@thinkfeel
```
Claude Code users can install the same plugin from the ThinkFeel marketplace. Run `/reload-plugins` or start a new Claude Code session after installation.
```bash theme={null}
npm i @curvelabs.org/thinkfeel
```
The ThinkFeel SDK and CLI use the site origin for `THINKFEEL_BASE_URL`, such as `https://playground.curvelabs.org`.
OpenAI-compatible clients use `https://playground.curvelabs.org/api/v1`.
## Where to start
Request access, set up keys, and send your first ThinkFeel request.
Learn supported API key headers, environment variables, and security rules.
Review request and response details for each endpoint.
Start from working example projects.
## Version
ThinkFeel API documentation version: `0.1.3 Beta`
Last updated: July 7, 2026
# Best practices
Source: https://docs.curvelabs.org/operations/best-practices
Keep ThinkFeel integrations clear, efficient, and secure.
## Maintain conversation context
To keep multi-turn conversations consistent, always include previous messages.
```javascript theme={null}
const conversation = [];
// First exchange
conversation.push({ role: 'user', content: 'What do you do for fun?' });
let response = await thinkFeel.generate({ messages: conversation });
conversation.push({ role: 'assistant', content: response.finalReply });
// Second exchange - includes full history
conversation.push({ role: 'user', content: 'That sounds cool!' });
response = await thinkFeel.generate({ messages: conversation });
```
## Trim long conversations
For very long conversations, consider truncating older messages to reduce latency.
```javascript theme={null}
function trimConversation(messages, maxMessages = 20) {
if (messages.length <= maxMessages) return messages;
return messages.slice(-maxMessages);
}
```
## Never expose API keys
Do not put API keys in client-side or public code.
Do not hardcode keys:
```javascript theme={null}
const response = await fetch(url, {
headers: {
'x-api-key': 'abc123...',
},
});
```
Use managed environment variables or secrets:
```javascript theme={null}
const response = await fetch(url, {
headers: {
'x-api-key': process.env.THINKFEEL_API_KEY,
},
});
```
# Error handling
Source: https://docs.curvelabs.org/operations/errors
Handle ThinkFeel status codes and errors.
The API uses standard HTTP status codes and returns detailed error messages.
## Status codes
| Status | Meaning | Description |
| ------ | --------------------- | ------------------------------------------------------------------------- |
| `200` | Success | Request completed successfully |
| `400` | Bad Request | Malformed JSON or invalid body on `/completions` and `/personify` |
| `401` | Unauthorized | Invalid or missing API key |
| `402` | Payment Required | Payment challenge for enabled self-serve personas when no API key is sent |
| `403` | Forbidden | API key is not associated with an active billing account |
| `404` | Not Found | Invalid persona ID |
| `422` | Unprocessable Entity | Invalid request body or parameters |
| `429` | Too Many Requests | Insufficient balance or quota exhausted on API-key billing |
| `500` | Internal Server Error | Server error. Contact support if persistent |
## ThinkFeel endpoint validation errors
The `/generate` and `/personify` endpoints return plain text for most validation and authentication errors.
```text theme={null}
Unknown `personaId` was provided in the body.
```
Quota and unexpected server errors may return JSON, so clients should check the response content type before parsing errors.
Requests without API-key headers may also receive a payment challenge instead of a JSON or plain-text error when self-serve payment access is enabled for the target persona.
If a persona is not enabled for self-serve payment access, send an API key header. Unauthenticated requests can return `404` instead of confirming persona availability.
## OpenAI-compatible error format
```json theme={null}
{
"error": {
"message": "Missing required `model`.",
"type": "invalid_request_error",
"param": "model",
"code": null
}
}
```
# Rate limits
Source: https://docs.curvelabs.org/operations/rate-limits
Understand early access beta request limits.
During the early access beta, API keys created in Playground use these default rate limits:
| Window | Limit |
| ---------- | -----------: |
| Per minute | 25 requests |
| Per hour | 100 requests |
| Per day | 500 requests |
These limits are subject to review and may be increased based on your use case. Contact Curve Labs if you need higher
limits.
# Quickstart
Source: https://docs.curvelabs.org/quickstart
Get approved, configure ThinkFeel, and send your first request.
ThinkFeel is available through the Curve Labs early access beta program.
Fill out the [request access form](https://curvelabs.org/request-api-access) with:
* Your name and email
* Company or project name
* Intended use case and target audience
* Expected usage volume
* Desired persona characteristics
The Curve Labs team reviews applications, typically within 2-5 days. After approval, the team schedules a 30-minute onboarding call to discuss your use case, craft a custom persona, define persona traits and communication style, whitelist your email for Playground access, and provide your unique `personaId`.
After onboarding, create API keys in one of these ways:
* Use `/keys` in [Playground](https://playground.curvelabs.org) for the browser UI
* Run `thinkfeel login --persona-id YOUR_THINKFEEL_PERSONA_ID` for standalone CLI profile setup
* Install the ThinkFeel Plugin for Codex or Claude Code from [CLI + Agents](/cli-agents) for agent-managed env-file setup
## Install the SDK and CLI
```bash theme={null}
npm i @curvelabs.org/thinkfeel
```
## Send your first request
```bash theme={null}
curl -X POST https://playground.curvelabs.org/api/v1/generate \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY_HERE" \
-d '{
"personaId": "YOUR_PERSONA_ID",
"messages": [
{
"role": "user",
"content": "Hello!"
}
]
}'
```
## What beta includes
Persona design is part of onboarding.
Personas are designed with explicit boundaries and safety behavior.
Beta pricing stays close to the cost of the underlying models.
Beta users get direct engineering support.
Ready to build? Start from [Authentication](/authentication), then choose either the ThinkFeel
[`/generate`](/api-reference/generate) endpoint or the OpenAI-compatible [`/completions`](/api-reference/completions)
endpoint.
# SDK examples
Source: https://docs.curvelabs.org/sdk-examples
Generate ThinkFeel replies from JavaScript, TypeScript, Python, Ruby, and Go REST clients.
These direct REST examples call `https://playground.curvelabs.org/api/v1/generate` with `THINKFEEL_API_KEY` and `THINKFEEL_PERSONA_ID`.
```javascript theme={null}
const axios = require("axios");
async function generateResponse(messages, includeVariations = false) {
try {
const response = await axios.post(
"https://playground.curvelabs.org/api/v1/generate",
{
personaId: process.env.THINKFEEL_PERSONA_ID,
messages: messages,
includeVariations: includeVariations,
},
{
headers: {
"Content-Type": "application/json",
"x-api-key": process.env.THINKFEEL_API_KEY,
},
}
);
return response.data;
} catch (error) {
console.error("Error:", error.response?.data || error.message);
throw error;
}
}
const messages = [
{ role: "user", content: "Hey! What do you think about AI?" },
];
generateResponse(messages).then((data) => {
console.log("AI Response:", data.result.finalReply);
});
```
```typescript theme={null}
interface TextPart {
type: "text";
text: string;
}
interface Message {
role: "user" | "assistant" | "system" | "developer";
content: string | TextPart[];
timestamp?: number | string;
createdAt?: number | string;
created_at?: number | string;
}
interface GenerateResponse {
status: "success";
rateLimits?: Array<{ limit: string; remaining: number | null }>;
result: {
finalReply: string;
chunks: string[];
replyChoices?: string[];
};
}
class ThinkFeelClient {
private apiKey: string;
private baseUrl = "https://playground.curvelabs.org/api/v1";
constructor(apiKey: string) {
this.apiKey = apiKey;
}
async generate(
personaId: string,
messages: Message[],
includeVariations = false
): Promise {
const response = await fetch(`${this.baseUrl}/generate`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"x-api-key": this.apiKey,
},
body: JSON.stringify({
personaId,
messages,
includeVariations,
}),
});
if (!response.ok) {
throw new Error(`API Error: ${response.status}`);
}
return response.json();
}
}
```
```python theme={null}
import os
import requests
class ThinkFeelAPI:
def __init__(self, api_key: str):
self.api_key = api_key
self.base_url = "https://playground.curvelabs.org/api/v1"
def generate(self, persona_id: str, messages: list,
include_variations: bool = False):
headers = {
"Content-Type": "application/json",
"x-api-key": self.api_key
}
request_body = {
"personaId": persona_id,
"messages": messages,
"includeVariations": include_variations
}
response = requests.post(
f"{self.base_url}/generate",
json=request_body,
headers=headers
)
response.raise_for_status()
return response.json()
api = ThinkFeelAPI(api_key=os.environ.get("THINKFEEL_API_KEY"))
persona_id = os.environ.get("THINKFEEL_PERSONA_ID")
messages = [
{"role": "user", "content": "What's your favorite thing to do?"}
]
result = api.generate(persona_id=persona_id, messages=messages)
print(f"AI: {result['result']['finalReply']}")
```
```ruby theme={null}
require "net/http"
require "json"
require "uri"
class ThinkFeelAPI
def initialize(api_key)
@api_key = api_key
@base_url = "https://playground.curvelabs.org/api/v1"
end
def generate(persona_id, messages, include_variations: false)
uri = URI("#{@base_url}/generate")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Post.new(uri.path)
request["Content-Type"] = "application/json"
request["x-api-key"] = @api_key
request.body = {
personaId: persona_id,
messages: messages,
includeVariations: include_variations
}.to_json
response = http.request(request)
JSON.parse(response.body)
end
end
api = ThinkFeelAPI.new(ENV["THINKFEEL_API_KEY"])
persona_id = ENV["THINKFEEL_PERSONA_ID"]
messages = [
{ role: "user", content: "What do you think about art?" }
]
result = api.generate(persona_id, messages)
puts "AI: #{result["result"]["finalReply"]}"
```
```go theme={null}
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
type Message struct {
Role string `json:"role"`
Content string `json:"content"`
}
type GenerateRequest struct {
PersonaID string `json:"personaId"`
Messages []Message `json:"messages"`
IncludeVariations bool `json:"includeVariations,omitempty"`
}
type ThinkFeelClient struct {
APIKey string
BaseURL string
}
func NewThinkFeelClient(apiKey string) *ThinkFeelClient {
return &ThinkFeelClient{
APIKey: apiKey,
BaseURL: "https://playground.curvelabs.org/api/v1",
}
}
func (c *ThinkFeelClient) Generate(personaID string, messages []Message) error {
reqBody := GenerateRequest{
PersonaID: personaID,
Messages: messages,
}
jsonData, _ := json.Marshal(reqBody)
req, _ := http.NewRequest("POST", c.BaseURL+"/generate", bytes.NewBuffer(jsonData))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("x-api-key", c.APIKey)
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Println(string(body))
return nil
}
```
# Support and feedback
Source: https://docs.curvelabs.org/support
Get help with ThinkFeel API integration issues.
Curve Labs supports beta users directly. You can expect a response within 24 hours on weekdays.
## What to include
Include the following when reporting an issue:
1. Your API key prefix, first 8 characters only
2. Timestamp of the issue
3. Request and response examples with sensitive data removed
4. Error messages or unexpected behavior
5. What you already tried
Use the Curve Labs support form for API, onboarding, and billing questions.
# Use cases
Source: https://docs.curvelabs.org/use-cases
Common product patterns for ThinkFeel integrations.
Build support chatbots that understand tone and emotion, then respond with empathy and useful next steps.
Create companion applications for mental wellness with warm, validating, wellness-only personas and clear escalation
paths.
Develop interactive storytelling or roleplay experiences with immersive characters.
Build conversational language learning tools with encouraging and adaptive personas.
If your use case needs a specific voice, safety boundary, or domain context, include that in your [request
access](https://curvelabs.org/request-api-access) application.