Quickstart
You need three things to send a chat request:
- An API key with the
chat:companionscope. - A vector store (an Upstash Vector index) configured on your account.
- A request describing the persona and the conversation so far.
Base URL
All client endpoints live under https://api.phantomrouter.ai, with the /api/v1 prefix.
1. Authenticate
Every request carries your key as a Bearer token:
export PHANTOM_KEY="sk_your_api_key"
curl https://api.phantomrouter.ai/api/v1/me/vector-store \
-H "Authorization: Bearer $PHANTOM_KEY"
If the key is valid but no store is configured yet, you get 404 NOT_FOUND - that's expected,
you'll fix it in the next step. See Authentication for how keys and scopes work.
2. Configure your vector store
Phantom writes memories to your Upstash Vector index. Point it at the index's REST URL and token (a 1536-dimension index matching the default embedding model). Prefer a UI? Connect it from the console instead - see Connect your vector store for a screenshot walkthrough.
curl -X PUT https://api.phantomrouter.ai/api/v1/me/vector-store \
-H "Authorization: Bearer $PHANTOM_KEY" \
-H "Content-Type: application/json" \
-d '{
"provider": "upstash",
"url": "https://your-index.upstash.io",
"token": "your-upstash-rest-token"
}'
Phantom test-probes the connection before saving and encrypts the token at rest. See the vector store reference for details and constraints.
3. Send a chat turn
curl -X POST https://api.phantomrouter.ai/api/v1/companion/chat \
-H "Authorization: Bearer $PHANTOM_KEY" \
-H "Content-Type: application/json" \
-d '{
"chat_id": "user-42",
"persona": {
"name": "Luna",
"bio": "A warm, witty companion who loves astronomy and late-night talks.",
"chat_style": "playful, curious"
},
"messages": [
{ "role": "user", "content": "Hey Luna! I just hiked Mount Rainier. I'm Sam, by the way." }
]
}'
A successful response:
{
"reply": "Sam! Rainier is no joke - those switchbacks earn the view. ...",
"chat_id": "user-42",
"moderation": { "inbound_prohibited": false, "outbound_filtered": false },
"memory": { "retrieved_count": 0, "profile_used": false },
"extraction_enqueued": false
}
The first turn retrieves nothing (the store is empty) and may not extract yet - memory extraction
is cadence-gated (by default every 4 user turns). Keep the same chat_id across turns and
Phantom builds up a memory of "Sam" and "Mount Rainier" that surfaces in later conversations.
4. See it work
Reuse the same chat_id across turns and Phantom accumulates memory about the user. A few turns
later, ask a follow-up and watch memory.retrieved_count climb as earlier turns surface. The
Playground is the fastest way to drive this interactively - send turns
and inspect each request and response live.
Next steps
- Authentication - keys, scopes, and what
chat:companionunlocks. - Chat endpoint - the full request and response schema.
- Errors & limits - error envelope and rate limiting.