API Reference

Base URL: https://pramana.pages.dev/api

All endpoints return JSON. Authentication uses JWT bearer tokens obtained via OAuth. soft = accepts anonymous. auth = requires valid token.

Submit Result

POST /api/submit soft

Submit a single eval result.

Request body

FieldTypeRequiredDescription
model_idstringyesModel identifier, max 256 chars
prompt_idstringyesPrompt/eval identifier, max 256 chars
outputstringyesModel output, max 1MB
scorenumbernoScore in [0, 1]. Omit if unscored.
metadataobjectnoArbitrary key-value pairs

Example

curl -X POST https://pramana.pages.dev/api/submit \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $TOKEN" \
  -d '{
    "model_id": "gpt-4o",
    "prompt_id": "factuality-q42",
    "output": "The capital of France is Paris.",
    "score": 1.0,
    "metadata": {"suite": "factuality", "version": "1.2"}
  }'

Response

{
  "id": "uuid-v4",
  "status": "accepted",
  "timestamp": "2026-02-22T12:00:00Z"
}

Batch Submit

POST /api/submit/batch soft

Submit a batch of eval results (up to 1000). This is what pramana submit calls.

Request body

FieldTypeRequiredDescription
suite_versionstringyesVersion of the eval suite
suite_hashstringyesContent hash of the suite
model_idstringyesModel identifier
temperaturenumberyesTemperature used for generation
seednumber|nullnoRandom seed if deterministic
timestampstringyesISO 8601 timestamp
resultsarrayyesArray of submission objects (max 1000)

Chart Data

GET /api/data/chart

Returns pre-aggregated chart data. Single R2 GET, cached.

Response shape

{
  "data": [
    {
      "date": "2026-02-20",
      "gpt-4o": 32,
      "gpt-4o_prompts": 10,
      "gpt-4o_unique_outputs": 10,
      "gpt-4o_drifted": 0,
      "gpt-4o_consistency": 1.0
    }
  ],
  "models": ["gpt-4o", "claude-3.5-sonnet"],
  "total_submissions": 12840,
  "total_contributors": 47
}
Per model per date: model = submission count, model_prompts = unique prompts tested, model_unique_outputs = distinct output hashes, model_drifted = prompts whose output changed vs previous day, model_consistency = (prompts - drifted) / prompts. See Methodology.

User Summary

GET /api/user/me/summary auth

Returns the authenticated user's submission summary.

Response shape (UserSummaryJson)

{
  "version": 3,
  "submissions_by_date": {
    "2026-02-20": { "gpt-4o": 10, "claude-3.5-sonnet": 5 }
  },
  "model_submissions": { "gpt-4o": 340, "claude-3.5-sonnet": 180 },
  "total_submissions": 520
}

User Stats

GET /api/user/me/stats auth

Returns computed statistics for the authenticated user (derived from summary).

Delete User Data

DELETE /api/user/me auth

GDPR data deletion. Removes the user's summary JSON from R2. Archived CSV submissions are anonymized via hashed user IDs and not modified.

Compact (Admin/Cron)

POST /api/admin/compact auth

Triggered daily by GitHub Actions cron. Requires Authorization: Bearer $CRON_SECRET.

  1. Archive current buffer.csv.gz → _archive/YYYY-MM-DD.csv.gz
  2. Rebuild _aggregated/chart_data.json from all archives + historical parquet
  3. Reset buffer

Health Check

GET /api/health
{ "status": "ok" }

Authentication

OAuth flow endpoints. Used by the dashboard SPA, not by CLI directly.

EndpointDescription
GET /api/auth/providersList available OAuth providers
GET /api/auth/signin/:providerRedirect to OAuth provider
GET /api/auth/callback/:providerOAuth callback, sets JWT cookie
GET /api/auth/sessionReturns current session info
POST /api/auth/signoutClears JWT cookie

Storage Schema

CSV records stored in buffer and archive files (12 fields):

FieldTypeDescription
idstringUUID v4
timestampstringISO 8601
user_idstringSHA-256 derived from OAuth identity
model_idstringModel identifier
prompt_idstringPrompt/eval identifier
outputstringModel output text
output_hashstringSHA-256 of output
metadata_jsonstringJSON-encoded metadata
yearnumberPartition year
monthnumberPartition month
daynumberPartition day
scorenumber|nullScore in [0, 1]