Overview
The State API learns how the User side of your conversations writes and makes decisions, building a persistent State in the base model’s representation space without modifying its weights.
Once a State is built from your uploaded conversations, calling /completions generates the next User message for a given context. Each API key manages one State; the full OpenAPI specification is available at GET /openapi.yaml.
Authentication
All API requests are authenticated with an API key. To request a key, email hello@topovoid.com, then call /register once to activate it before making other calls.
Every endpoint except /register and /openapi.yaml requires an Authorization: Bearer $TOPOVOID_API_KEY header. Keep your API key on the server side—never embed it in browsers, mobile apps, or other client-side code.
POST/registerKey in the body
Activates and registers an API key. This endpoint is idempotent; calling it again with an already-registered key is a no-op.
| Parameter | Type | Description |
|---|---|---|
apikey |
string | Required. Your API key |
Returns {"ok": true, "user_id": "..."}.
curl https://api.topovoid.com/reflecho/register \
-H "Content-Type: application/json" \
-d "{\"apikey\": \"$TOPOVOID_API_KEY\"}"Upload conversations
POST/uploadBearer key
Uploads training conversations as a JSON array (optionally compressed with Content-Encoding: gzip). Uploading a conversation whose id already exists overwrites the previous version; setting value to null deletes that conversation.
| Parameter | Type | Description |
|---|---|---|
id |
string | Required. Unique conversation ID |
value |
object or null | Required. {"messages": [...]} to create or replace a conversation, or null to delete it |
Each entry in messages has the following shape:
| Parameter | Type | Description |
|---|---|---|
type |
string | Role tag: User, Assistant, AssistantThink or ToolCall |
content |
string | Message body |
name |
string | Tool name (required for ToolCall entries only) |
Every conversation must end with a User message, and consecutive User messages must be separated by at least one non-User turn. AssistantThink and ToolCall entries are preserved as context but excluded from the training loss.
Returns 204 No Content on success.
[
{
"id": "0199f1a2-6c3e-7d10-9a4b-2f8e5c1d7a30",
"value": {
"messages": [
{ "type": "User", "content": "Review the service entry point." },
{ "type": "Assistant", "content": "Config and start-up are coupled." },
{ "type": "User", "content": "Don't change it. Show me the tests." }
]
}
}
]curl https://api.topovoid.com/reflecho/upload \
-H "Authorization: Bearer $TOPOVOID_API_KEY" \
-H "Content-Type: application/json" \
--data-binary @sessions.jsonBuild a State
POST/flush-stateBearer key
Triggers a background build across all currently uploaded conversations. Once trained, the candidate State is promoted to active only after passing automated validation checks; if a build or check fails, your previously active State remains in service untouched.
Building a State requires at least 10 training examples, where each User turn preceded by at least one earlier message in the same conversation counts as one example. The request body is optional—any omitted hyperparameters fall back to the selected preset:
| Parameter | Type | Description |
|---|---|---|
preset |
string | fast (default) or full |
learning_rate |
number | Peak learning rate, in (0, 0.001] |
epochs |
integer | Maximum training epochs, from 1 to 10 |
train_token_budget |
integer or null | Cap on total training tokens (minimum 10,000, or null for no cap) |
| Preset | fast |
full |
|---|---|---|
| Training token limit | 300,000 | Unlimited |
| Maximum epochs | 2 | 2 |
| Max example length | 4,096 tokens | 8,192 tokens |
| Peak learning rate | 2e-4 | 1e-4 |
| Typical duration | ~9 minutes | Tens of minutes to a few hours |
Returns 202 Accepted with the same status payload as /state-status.
curl https://api.topovoid.com/reflecho/flush-state \
-H "Authorization: Bearer $TOPOVOID_API_KEY" \
-H "Content-Type: application/json" \
-d '{"preset": "full"}'Check build status
GET/state-statusBearer key
Returns the current status of the most recent build job. When waiting for a build to finish, polling every ~30 seconds is recommended.
| Field | Type | Description |
|---|---|---|
status |
string | Job status: building, finished or failed |
phase |
string | Current stage: queued, preparing, training, checking or activating |
progress |
object | Step-level training progress |
active |
object or null | Metadata for the currently active State (null if no State has been activated yet) |
reason |
string | Failure code when status is failed: no_dev_improvement (validation loss did not beat the base model; usually resolved by uploading more examples), generation_check_failed, consistency_check_failed, training_error or internal_error |
curl https://api.topovoid.com/reflecho/state-status \
-H "Authorization: Bearer $TOPOVOID_API_KEY"{
"status": "building",
"phase": "training",
"progress": { "attempt": 1, "step": 18, "total_steps": 37 },
"start_at": "2026-10-16T08:12:03.418+00:00",
"active": null
}Create a completion
POST/completionsBearer key
Generates the next User message for an ongoing conversation using your active State.
| Parameter | Type | Description |
|---|---|---|
id |
string | Required. Unique request ID |
context |
string | Required. Conversation transcript formatted as described in Context format |
systemprompt |
string | Optional. Overrides the default system prompt (note: States are trained under the default system prompt) |
thinking |
string | Optional. Reasoning effort: off (default), low, medium or xhigh |
| Response field | Type | Description |
|---|---|---|
response |
string | Generated User message |
time |
integer | End-to-end generation latency in milliseconds |
confidence |
number or null | Geometric mean of the output token probabilities (null when thinking is enabled) |
curl https://api.topovoid.com/reflecho/completions \
-H "Authorization: Bearer $TOPOVOID_API_KEY" \
-H "Content-Type: application/json" \
-d '{"id": "0199f1a2-8b40-7c55-a1d3-5e7f00c2b9a4",
"context": "User: All tests pass. Ready to commit.\n\nAssistant:"}'{
"response": "Cover the failure paths too.",
"time": 1842,
"confidence": 0.74
}Context format
Because the underlying model always completes after an Assistant: prefix while your State models the User, role labels in context are swapped relative to /upload: format prior User turns as Assistant: and prior Assistant turns as User:. Separate turns with a blank line, omit AssistantThink and ToolCall entries, and end the string with Assistant: so the model generates the next user reply:
Assistant: Review the service entry point.
User: Config and start-up are coupled.
Assistant: Don't change it. Show me the tests.
User: All tests pass. Ready to commit.
Assistant:Errors
Failed requests return a JSON body of the form {"code": "...", "message": "..."}. Branch on code rather than the human-readable message:
| Status | code |
Cause |
|---|---|---|
| 400 | bad_request |
Malformed parameter, fewer than 10 training examples, or prompt exceeding the context limit |
| 401 | unauthorized |
Missing or invalid API key |
| 404 | not_found |
API key not registered, no conversations uploaded yet, or no build has been run |
| 405 | method_not_allowed |
Unsupported HTTP method for this path |
| 409 | conflict |
A build is already queued or running, or /completions was called before a State finished building |
| 413 | payload_too_large |
Request payload exceeds size limits |
| 415 | unsupported_content_encoding |
Content encoding other than gzip |
| 500 | internal_error |
Unexpected server-side error |
| 503 | service_unavailable |
Service temporarily overloaded or unavailable |
Limits
- Prompts are capped at roughly 32,000 tokens.
/completionsreturns a single JSON response; token streaming is not supported.- Build and completion jobs are processed sequentially and may queue under load.
- Uploaded conversations persist until explicitly deleted via
/uploadwithvalue: null. Completion requests and responses are logged on the server.