Reference
Import existing candidates and preferences
Seed an arena from real candidates and pairwise choices without replacing data or inventing votes.
The same import from first run, Settings, and the CLI#
Run preferences import --file FILE from a terminal. In the web app, choose Open import settings during the first-run walkthrough or open Settings later and select the same JSON file. Settings also carries Show the walkthrough again, so the action stays reachable on replay. Both surfaces call authenticated POST /api/imports, and that route calls one product-owned transaction; the browser does not parse or store a second version of the import.
An operator bearer may import public or private arenas. A signed-in organization owner or admin may import a private arena when its organization_id names that membership. An account cannot import a public arena. Omitting the import is valid and leaves an empty usable product.
preferences import --file preferences.json
preferences import --file preferences.json --jsonPOST /api/imports
Content-Type: application/json
Authorization: Bearer <operator or signed-in account token>
<preferences-import-v1 document>preferences-import-v1#
The root has exactly format, source_id, arena and categories. source_id is the durable identity of this export. An arena has id, name, visibility and, for private visibility, organization_id. Every category has id, name, kind, non-empty candidates and optional preferences.
A text candidate has id, generator, text and optional attributes. An image or html candidate has id, generator, content_type, canonical content_base64 and optional attributes. Every preference has id, participant_id, left_candidate_id, right_candidate_id, chosen and decided_ms. chosen is left, right, tie or skip; both candidate ids must name different candidates in that category.
{
"format": "preferences-import-v1",
"source_id": "survey-export-2026-09-05",
"arena": {
"id": "existing-copy",
"name": "Existing copy study",
"visibility": "public"
},
"categories": [{
"id": "tagline",
"name": "Tagline",
"kind": "text",
"candidates": [
{"id": "a", "generator": "writer-a", "text": "Build what people choose."},
{"id": "b", "generator": "writer-b", "text": "Measure the better line."}
],
"preferences": [{
"id": "choice-001",
"participant_id": "participant-17",
"left_candidate_id": "a",
"right_candidate_id": "b",
"chosen": "left",
"decided_ms": 1840
}]
}]
}Duplicates, failures, and retained state#
Accepted candidates retain their source candidate id, generator, content and attributes. The source id is retained on the submission and candidate metadata. Each source participant maps to one distinct voter across the imported categories, and every supplied preference becomes a pair and its recorded choice rather than a generated vote.
The importer writes all rows in one database transaction and recomputes each affected category ranking inside it. Existing unrelated data is never deleted or updated. Imported candidates and choices are therefore available immediately to voting, leaderboards, profiles and ster-pairs export.
| Input or state | Result |
|---|---|
Exact replay of one source_id | No new rows; every accepted arena, category, candidate, participant and preference is reported unchanged |
Different document with the same source_id | HTTP 409 conflict; no rows from the request remain |
Existing arena or category with different fields | HTTP 409 conflict; existing data is preserved |
Duplicate candidate id, preference id, or participant comparison | HTTP 400 refusal before mutation |
Missing candidate reference, equal sides, invalid chosen value, invalid base64, or unsupported field | HTTP 400 refusal before mutation |
Accepted document | Per-category submission ids plus imported, unchanged, conflicting and rejected counts |