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:

  1. Authenticate with an API key
  2. POST a messages array with a strategy ID
  3. Receive the distilled messages array + metadata
  4. 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

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

POST /distill

Transform a messages array using a named strategy. This is the core endpoint.

Request body

FieldTypeRequiredNotes
messagesarrayno*Standard OpenAI/Anthropic messages format. Each item: {"role": "user"|"assistant", "content": "..."}
textstringno*Plain text input (alternative to messages). Use for raw text that isn't in messages format.
strategy_idstringnoDefaults to "compress". ID of a built-in or community strategy. See Strategies.
injectstringnoMax 400 chars. Appended to the strategy's system prompt for this call only. Logged to telemetry.
target_tokenintegernoTarget output size in tokens (compress pathway only). Range: 50–131072. Default: 131072.
postprocess_promptstringnoOptional 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

CodeMeaning
400Malformed request — missing fields, inject too long, or inject contains a disallowed phrase
401Missing or invalid API key
402Insufficient credits — response body includes topup_url
404strategy_id not found
429Rate limit exceeded
500Provider call failed or internal error

GET /strategies

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 /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

CodeMeaning
404Strategy not found

POST /feedback

POST /feedback

Submit a quality rating for a distillation result. Use the call_id from the /distill response metadata.

Request body

FieldTypeRequiredNotes
call_idstringyesUUID from the distillation metadata
ratingenumyes"bad", "ok", "good"
reason_categoryenumyesSee categories below
reason_textstringnoMax 500 characters. Free text.
strategy_idstringyesStrategy used in the rated call
strategy_versionstringyesVersion from the distillation metadata
inject_usedbooleanyesWhether inject was provided in the rated call
inject_textstringnoInject value if used, for blob storage
messages_inarraynoOriginal messages for strategy improvement analysis
messages_outarraynoDistilled messages for comparison

Reason categories

ValueWhen to use
too_aggressiveToo much was removed
lost_codeCode blocks were dropped or mangled
lost_decisionsKey decisions or conclusions were removed
wrong_focusSummarized the wrong parts
format_brokenOutput messages structure was malformed
otherUse with reason_text

Response (204 No Content)

No response body on success.

GET /balance

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

POST /balance/topup

Initiate a Stripe checkout session to deposit credits.

Request body

FieldTypeRequiredNotes
amount_usdnumberyesAmount 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

IDNameBest forCompression
summarizeAggressive SummarizeLong general conversations8–15%
technical_denseTechnical DenseCoding, debugging, architecture10–20%
extract_decisionsExtract DecisionsPlanning, review sessions5–10%

Strategy schema (key fields)

FieldTypeNotes
idstringLowercase with underscores. Filename must match.
namestringHuman-readable display name
versionstringSemver. Increment minor on system_prompt changes.
descriptionstringOne sentence — what it preserves, what it removes.
default_providerstringProvider key. Currently: gemini-flash, claude-haiku
expected_compressionstringRough ratio range, e.g. "0.10-0.20"
system_promptstringFull 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.

TierWhoRate
founderFirst 20 usersDiscounted — locked in at signup
earlyEarly access usersReduced rate
standardAll other usersStandard 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.