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
| Field | Type | Required | Description |
|---|---|---|---|
model_id | string | yes | Model identifier, max 256 chars |
prompt_id | string | yes | Prompt/eval identifier, max 256 chars |
output | string | yes | Model output, max 1MB |
score | number | no | Score in [0, 1]. Omit if unscored. |
metadata | object | no | Arbitrary 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
| Field | Type | Required | Description |
|---|---|---|---|
suite_version | string | yes | Version of the eval suite |
suite_hash | string | yes | Content hash of the suite |
model_id | string | yes | Model identifier |
temperature | number | yes | Temperature used for generation |
seed | number|null | no | Random seed if deterministic |
timestamp | string | yes | ISO 8601 timestamp |
results | array | yes | Array 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.
- Archive current buffer.csv.gz → _archive/YYYY-MM-DD.csv.gz
- Rebuild _aggregated/chart_data.json from all archives + historical parquet
- Reset buffer
Health Check
GET /api/health
{ "status": "ok" }
Authentication
OAuth flow endpoints. Used by the dashboard SPA, not by CLI directly.
| Endpoint | Description |
|---|---|
GET /api/auth/providers | List available OAuth providers |
GET /api/auth/signin/:provider | Redirect to OAuth provider |
GET /api/auth/callback/:provider | OAuth callback, sets JWT cookie |
GET /api/auth/session | Returns current session info |
POST /api/auth/signout | Clears JWT cookie |
Storage Schema
CSV records stored in buffer and archive files (12 fields):
| Field | Type | Description |
|---|---|---|
id | string | UUID v4 |
timestamp | string | ISO 8601 |
user_id | string | SHA-256 derived from OAuth identity |
model_id | string | Model identifier |
prompt_id | string | Prompt/eval identifier |
output | string | Model output text |
output_hash | string | SHA-256 of output |
metadata_json | string | JSON-encoded metadata |
year | number | Partition year |
month | number | Partition month |
day | number | Partition day |
score | number|null | Score in [0, 1] |