API Documentation
contextspa is a stateless context transformation API. Pass a messages array, get back a distilled version. No storage, no memory, no state between calls.
Base URL: https://contextspa.com/api
Overview
Every call to /distill follows the same pattern:
- Authenticate with an API key
- POST a messages array with a strategy ID
- Receive the distilled messages array + metadata
- Drop the output into your next LLM call
contextspa never touches your downstream LLM. It transforms context and gets out of the way.
Authentication
All requests (except GET /strategies) require an API key sent as a Bearer token:
Authorization: Bearer csp_<your-api-key>
API keys are prefixed csp_. Keys are shown once on creation — store them securely. If lost, generate a new one from your account page.
Quickstart
curl -X POST https://contextspa.com/api/distill \
-H "Authorization: Bearer csp_your_key" \
-H "Content-Type: application/json" \
-d '{
"messages": [
{"role": "user", "content": "How do I reverse a linked list?"},
{"role": "assistant", "content": "You can reverse a linked list iteratively by..."}
],
"strategy_id": "technical_dense"
}'
GET /health
Liveness and dependency check. Always returns HTTP 200 — inspect the body for degraded state. No authentication required.
Response (200)
{
"status": "ok",
"version": "0.1.0",
"checks": {
"storage_reachable": true,
"gemini_key_present": true,
"stripe_key_present": true
}
}
When storage_reachable is false, the API will fail on any call that reads or writes user data. The status field will be "degraded".
POST /distill
Transform a messages array using a named strategy. This is the core endpoint.
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
| messages | array | no* | Standard OpenAI/Anthropic messages format. Each item: {"role": "user"|"assistant", "content": "..."} |
| text | string | no* | Plain text input (alternative to messages). Use for raw text that isn't in messages format. |
| strategy_id | string | no | Defaults to "compress". ID of a built-in or community strategy. See Strategies. |
| inject | string | no | Max 400 chars. Appended to the strategy's system prompt for this call only. Logged to telemetry. |
| target_token | integer | no | Target output size in tokens (compress pathway only). Range: 50–131072. Default: 131072. |
| postprocess_prompt | string | no | Optional LLM postprocessing instruction applied after compression. |
* Provide either messages or text — at least one is required.
Example request
{
"messages": [
{"role": "user", "content": "..."},
{"role": "assistant", "content": "..."}
],
"strategy_id": "technical_dense",
"inject": "preserve all mentions of variable authToken exactly as written"
}
Response (200)
{
"messages": [
{"role": "user", "content": "..."},
{"role": "assistant", "content": "..."}
],
"metadata": {
"strategy_id": "technical_dense",
"strategy_version": "1.0.0",
"provider": "gemini-flash",
"tokens_in": 4200,
"tokens_out": 610,
"compression_ratio": 0.145,
"inject_used": true,
"call_id": "550e8400-e29b-41d4-a716-446655440000"
}
}
Error responses
| Code | Meaning |
|---|---|
| 400 | Malformed request — missing fields, inject too long, or inject contains a disallowed phrase |
| 401 | Missing or invalid API key |
| 402 | Insufficient credits — response body includes topup_url |
| 404 | strategy_id not found |
| 429 | Rate limit exceeded |
| 500 | Provider call failed or internal error |
GET /strategies
List all available strategies. No authentication required.
Response (200)
{
"strategies": [
{
"id": "summarize",
"name": "Aggressive Summarize",
"description": "Reduces conversation to decisions, outcomes, and open questions.",
"tier": "builtin",
"version": "1.0.0"
},
{
"id": "technical_dense",
"name": "Technical Dense",
"description": "Preserves all code, errors, and decisions. Strips conversational filler.",
"tier": "builtin",
"version": "1.0.0"
},
{
"id": "extract_decisions",
"name": "Extract Decisions",
"description": "Extracts only explicit decisions and their rationale.",
"tier": "builtin",
"version": "1.0.0"
}
]
}
GET /strategies/{id}
Get the full config for a single strategy, including its system prompt. No authentication required.
Response (200)
Full strategy object — see Strategy schema below.
Error responses
| Code | Meaning |
|---|---|
| 404 | Strategy not found |
POST /feedback
Submit a quality rating for a distillation result. Use the call_id from the /distill response metadata.
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
| call_id | string | yes | UUID from the distillation metadata |
| rating | enum | yes | "bad", "ok", "good" |
| reason_category | enum | yes | See categories below |
| reason_text | string | no | Max 500 characters. Free text. |
| strategy_id | string | yes | Strategy used in the rated call |
| strategy_version | string | yes | Version from the distillation metadata |
| inject_used | boolean | yes | Whether inject was provided in the rated call |
| inject_text | string | no | Inject value if used, for blob storage |
| messages_in | array | no | Original messages for strategy improvement analysis |
| messages_out | array | no | Distilled messages for comparison |
Reason categories
| Value | When to use |
|---|---|
| too_aggressive | Too much was removed |
| lost_code | Code blocks were dropped or mangled |
| lost_decisions | Key decisions or conclusions were removed |
| wrong_focus | Summarized the wrong parts |
| format_broken | Output messages structure was malformed |
| other | Use with reason_text |
Response (204 No Content)
No response body on success.
GET /balance
Get the authenticated user's current credit balance and tier.
Response (200)
{
"user_id": "...",
"pricing_tier": "standard",
"balance_usd": 8.42
}
POST /balance/topup
Initiate a Stripe checkout session to deposit credits.
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
| amount_usd | number | yes | Amount to deposit in USD. Minimum: $5.00 |
Response (200)
{"checkout_url": "https://checkout.stripe.com/..."}
Redirect the user to checkout_url to complete payment. Credits are added after Stripe confirms the payment via webhook.
Strategies
A strategy is a named, versioned JSON config that defines how a messages array is transformed. Strategies are declarative — they contain a system prompt and metadata, not executable code.
Built-in strategies
| ID | Name | Best for | Compression |
|---|---|---|---|
| summarize | Aggressive Summarize | Long general conversations | 8–15% |
| technical_dense | Technical Dense | Coding, debugging, architecture | 10–20% |
| extract_decisions | Extract Decisions | Planning, review sessions | 5–10% |
Strategy schema (key fields)
| Field | Type | Notes |
|---|---|---|
| id | string | Lowercase with underscores. Filename must match. |
| name | string | Human-readable display name |
| version | string | Semver. Increment minor on system_prompt changes. |
| description | string | One sentence — what it preserves, what it removes. |
| default_provider | string | Provider key. Currently: gemini-flash, claude-haiku |
| expected_compression | string | Rough ratio range, e.g. "0.10-0.20" |
| system_prompt | string | Full prompt sent to the provider. The core of the strategy. |
Community strategy submissions are coming soon. Full schema reference: docs/STRATEGIES.md.
Inject
The optional inject field appends a short instruction to the strategy's system prompt for a single call. Use it when you need a one-off override without creating a new strategy.
"inject": "preserve all mentions of variable authToken exactly as written"
Limits: Max 400 characters. Logged to telemetry. Phrases like "ignore previous instructions" are rejected with HTTP 400.
The inject is appended after the strategy's system prompt with a clear separator. It cannot override the core transformation rules, only extend them.
Errors
All error responses use standard HTTP status codes with a JSON body:
{"detail": "Strategy not found: my_strategy_id"}
The 402 response includes an additional field:
{"detail": "Insufficient credits", "topup_url": "https://contextspa.com/billing"}
Pricing
contextspa uses a prepay credit model. Deposit credits, spend them per call.
| Tier | Who | Rate |
|---|---|---|
| founder | First 20 users | Discounted — locked in at signup |
| early | Early access users | Reduced rate |
| standard | All other users | Standard rate |
Each call costs a platform fee plus token cost × provider rate. Minimum deposit: $5.00. Full pricing details published at first paying customer.
Use GET /balance to check your balance. Use POST /balance/topup to deposit.