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.

Import from the CLI
preferences import --file preferences.json
preferences import --file preferences.json --json
Import through the authenticated API
POST /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.

A retained text arena with one real choice
{
  "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.

What the importer does
Input or stateResult
Exact replay of one source_idNo new rows; every accepted arena, category, candidate, participant and preference is reported unchanged
Different document with the same source_idHTTP 409 conflict; no rows from the request remain
Existing arena or category with different fieldsHTTP 409 conflict; existing data is preserved
Duplicate candidate id, preference id, or participant comparisonHTTP 400 refusal before mutation
Missing candidate reference, equal sides, invalid chosen value, invalid base64, or unsupported fieldHTTP 400 refusal before mutation
Accepted documentPer-category submission ids plus imported, unchanged, conflicting and rejected counts