Skip to content

LLARS v1 Conference Manager API

Version: 1.0 | Last updated: October 2026

Programmatic REST API for the conference manager: read research groups, maintain conference series and editions (confirmed dates and estimated windows), manage papers with authors, arXiv/DOI links and submission history, and start, watch and cancel the AI refresh. All endpoints live under /api/v1/conference-manager/*.

Authentication

Identical to the Scenario API: X-API-Key (personal or system) or OAuth bearer. See v1 Scenario API. Personal keys are created under Settings → API Keys, where you pick the scopes conference:read and/or conference:write.

Scopes

Scope Allows RBAC fallback (OAuth, keys without scopes)
conference:read every GET under /api/v1/conference-manager/* feature:conference_manager:view
conference:write every POST / PATCH / PUT / DELETE, including starting and cancelling the AI refresh; implies read feature:conference_manager:edit
admin:* overrides everything admin:permissions:manage

Conference scopes are strictly separate from scenario:* and chatbot:*.

Scope and permission together: a scope narrows a key, it never widens it. On top of the scope, the account that owns the key must hold the matching feature permission (feature:conference_manager:view to read, …:edit to write; admins always). Revoke the permission and the account's conference keys stop working immediately (403).

Research groups and visibility

Every series, edition, paper and AI run belongs to exactly one research group. Every call is checked against that group:

Situation Response
You are a member (role owner or member) read and write
You are a member with role viewer read; writes → 403
You are not a member 404 — exactly like an ID that does not exist
Admin / system key all groups

The 404 for foreign groups is deliberate: status codes must not reveal which IDs exist in other groups. References in a body (series_id, conference_id) must point into the same group as the object, otherwise 404 as well. Legacy records without a group are visible to admins only; the API never creates group-less records (group_id is required on create and cannot be changed later).

Response and error format

Successful responses carry "success": true and the object under its name (conference, series, paper, run, research_group). Lists are paginated:

{ "success": true, "conferences": [ ... ], "total": 42, "limit": 50, "offset": 0 }

limit is at most 200 (larger values are capped, default 50), offset starts at 0. Errors look the same everywhere:

{
  "success": false,
  "error": "Invalid request body — Value error, submission_window_start must be on or before submission_window_end",
  "error_type": "validation_error",
  "details": { "errors": [ { "loc": [], "msg": "...", "type": "value_error" } ] }
}
Status Meaning
200 / 201 read / created
202 AI run accepted (runs in the background)
400 invalid body or query parameter (unknown field, bad date, window with start > end, invalid DOI …)
401 missing or invalid key
403 scope missing, feature permission missing, or group is read-only (viewer)
404 does not exist or belongs to a foreign group
409 conflict: edition (acronym, year) or series acronym already exists; an AI run is already active in the group
503 AI refresh is not available on this server

Dates: confirmed or estimated

Every edition has three milestones. Each can have an exact date (confirmed) and/or a window (estimated):

Milestone Exact date Window
Submission submission_deadline submission_window_start, submission_window_end
Author notification notification_date notification_window_start, notification_window_end
Conference start_date, end_date conference_window_start, conference_window_end
  • Formats: exact dates are naive ISO datetimes YYYY-MM-DDTHH:MM:SS (always returned that way). On write, a plain date (2027-05-15 → 00:00:00) or an offset (Z, +02:00) is accepted too; an offset is converted to UTC and then dropped. Window bounds are plain dates YYYY-MM-DD — a time of day is a 400 here.
  • Rules: start ≤ end per window and start_date ≤ end_date, otherwise
  • A PATCH that changes only one bound is checked against the stored counterpart. Unparsable values are always a 400 — never a silent erase.
  • The exact date wins: once the real date is announced, set it via PATCH. The window stays as history; the milestone counts as confirmed from then on.
  • Derived fields in the response: milestones per milestone with status (confirmed | estimated | unknown), date or start/end, and window_start/window_end; is_estimated is true as soon as one milestone is only estimated. estimation_basis (free text, e.g. "derived from 2023–2026") and estimation_confidence (low | medium | high) explain the estimate. Also included: core_url (CORE portal), maps_url (Google Maps), last_refreshed_at and last_refresh_summary.

PATCH semantics (everywhere): only the fields you send are changed. An explicit null clears an optional field (e.g. "notes": null). Required fields (name, acronym, year, title) and an edition's core_ranking cannot become null — send "Unranked" to remove the ranking. Unknown fields are a 400.

Endpoints

Research groups

Method Path Scope Purpose
GET /api/v1/conference-manager/research-groups conference:read Your groups with user_role (admins: all)
GET /api/v1/conference-manager/research-groups/{id} conference:read Group with user_role, stats and members

Groups and memberships are managed in the UI; the v1 API does not create groups.

Series

Method Path Scope Purpose
GET /api/v1/conference-manager/series?group_id=&search= conference:read Series (without group_id: all accessible groups)
POST /api/v1/conference-manager/series conference:write Create a series (group_id, name, acronym, optional core_ranking, website_url, keywords, notes)
GET /api/v1/conference-manager/series/find?acronym=&group_id= conference:read Series by acronym (case-insensitive); series: null if none matches
GET /api/v1/conference-manager/series/{id} conference:read Series with its editions
PATCH /api/v1/conference-manager/series/{id} conference:write Partial update
DELETE /api/v1/conference-manager/series/{id} conference:write Delete; editions stay and only lose the series link

Series acronyms are unique server-wide — a taken acronym yields 409.

Conferences (editions)

Method Path Scope Purpose
GET /api/v1/conference-manager/conferences conference:read Editions (filters below)
POST /api/v1/conference-manager/conferences conference:write Create an edition (group_id, name, acronym, year + any field from "Dates")
GET /api/v1/conference-manager/conferences/{id} conference:read Read an edition
PATCH /api/v1/conference-manager/conferences/{id} conference:write Partial update of every field including windows
DELETE /api/v1/conference-manager/conferences/{id} conference:write Delete; papers stay and only lose the conference link
GET /api/v1/conference-manager/conferences/{id}/papers conference:read Papers of this edition

Further edition fields: series_id, core_ranking (A*, A, B, C, Unranked), city, country, website_url (http(s):// only), keywords (≤ 50), notes. (acronym, year) is unique server-wide → 409.

Filters for GET /conferences (all optional, combinable):

Parameter Effect
group_id only this group (foreign group → 404); without it: all accessible groups
year edition year
search substring of name, acronym, city or country
estimated true: only editions with at least one estimated milestone; false: none of those
from, to YYYY-MM-DD; an edition matches when its overall span (all dates and windows) overlaps [from, to]. Editions without any date drop out.
core_ranking A*, A, B, C, Unranked

Sorted by submission deadline (estimated editions by their window start), editions with neither at the end.

Papers, authors, submissions

Method Path Scope Purpose
GET /api/v1/conference-manager/papers?group_id=&conference_id=&status=&search= conference:read Papers (search in title and description)
POST /api/v1/conference-manager/papers conference:write Create a paper
GET /api/v1/conference-manager/papers/{id} conference:read Paper with authors, submissions, conference
PATCH /api/v1/conference-manager/papers/{id} conference:write Partial update; authors replaces the list
DELETE /api/v1/conference-manager/papers/{id} conference:write Delete including authors and submissions
PUT /api/v1/conference-manager/papers/{id}/authors conference:write Replace the whole author list
POST /api/v1/conference-manager/papers/{id}/submissions conference:write Append a submission
PATCH /api/v1/conference-manager/papers/{id}/submissions/{sid} conference:write Update a submission
DELETE /api/v1/conference-manager/papers/{id}/submissions/{sid} conference:write Delete a submission
  • Fields: group_id, title, status (planning, in_progress, submitted, accepted, rejected, published), conference_id, overleaf_url, external_url, arxiv_url, doi, keywords, description, notes, authors.
  • arXiv and DOI: arxiv_url accepts arXiv:2401.12345, 2401.12345 or an arxiv.org link and stores https://arxiv.org/abs/<id>. doi accepts doi:10.…, https://doi.org/10.… or the bare DOI and stores the bare DOI. The response also contains arxiv_id and doi_url (https://doi.org/<doi>). Invalid values are a 400.
  • Authors: ordered list; each entry has exactly one of user_id, username (LLARS account) or external_name (external person), optionally author_order (default: list position) and is_corresponding. Unknown or duplicated accounts are a 400; at most 50 authors.
  • Submissions: conference_id, status (submitted, accepted, rejected, withdrawn), submitted_at, decided_at (exact dates), notes. The paper takes over conference and status of the newest submission. A submission is reachable only through its own paper — a foreign sid in the path yields 404.

AI refresh

Method Path Scope Purpose
POST /api/v1/conference-manager/conferences/refresh conference:write Start a run for a group → 202
POST /api/v1/conference-manager/conferences/{id}/refresh conference:write Run for exactly this edition → 202
GET /api/v1/conference-manager/refresh-runs?group_id=&limit= conference:read Latest runs of the group, newest first, without progress (group_id required, limit ≤ 100, default 20)
GET /api/v1/conference-manager/refresh-runs/{id} conference:read One run with progress per edition
POST /api/v1/conference-manager/refresh-runs/{id}/cancel conference:write Request cancellation
POST /api/v1/conference-manager/refresh-runs/{id}/resume conference:write Resume a cancelled or failed run → 202

Start: body {"group_id": 1, "conference_ids": [12, 13], "include_past_months": 3}. Without conference_ids the run takes every edition of the group whose latest known date (exact or window) is at most include_past_months months in the past (0–36, default 3), plus editions without any date. With conference_ids exactly those (at most 200; they must belong to the group, otherwise 404). If a run is already active in the group you get 409 with details.run_id of the active run. The AI never runs on its own — every run is started manually.

Lifecycle: queued → running → done | failed | cancelled. Editions are processed one after another (web search, reading pages, LLM comparison with the stored data). Only values the AI finds as confirmed with a source overwrite confirmed dates; estimates only fill empty windows. For the ARR series the run creates missing cycles (with a confirmed submission deadline) as new editions.

Run object:

{
  "id": 7, "group_id": 1, "scope": "all", "conference_ids": [12, 13],
  "status": "running", "is_active": true, "started_by": "alice",
  "model_id": "Global/Mistral/Mistral-Small-3.2-24B-Instruct-2506",
  "started_at": "2026-10-02T09:15:00", "finished_at": null, "cancel_requested": false,
  "summary": { "total": 2, "done": 1, "changed": 1, "no_change": 0, "errors": 0 },
  "open_conference_ids": [],
  "progress": {
    "12": {
      "status": "done", "step": "…", "acronym": "EMNLP", "year": 2027, "name": "…",
      "sources": ["https://2027.emnlp.org/calls/main_conference_papers/"],
      "changes": [
        { "field": "submission_deadline", "old": null, "new": "2027-05-19T23:59:00",
          "confidence": "confirmed", "source_url": "https://2027.emnlp.org/…",
          "evidence": "Paper submission deadline: May 19, 2027" }
      ],
      "error": null, "started_at": "…", "finished_at": "…", "updated_at": "…"
    },
    "13": { "status": "searching", "step": "…", "sources": [], "changes": [], "error": null }
  }
}

progress[...].status per edition: queued, searching, reading, analyzing, done (with changes), no_change, error, skipped. An API client polls GET /refresh-runs/{id} every few seconds until is_active is false (the UI receives the same steps live via Socket.IO).

Cancel: sets cancel_requested; the run stops after the edition it is working on, remaining editions become skipped. A run that has already finished stays unchanged and the response is its final state.

Resume: a run can also end without a cancel, for example when the server stops the process during a deploy or restart; it is then failed (summary.error e.g. "Worker beendet"). For failed and cancelled runs open_conference_ids lists the editions that are still open: ended with an error (error) or skipped (skipped), but not deleted. For every other status the list is empty. POST /refresh-runs/{id}/resume starts a new run over exactly these editions with the same scope and answers 202 with {"success": true, "run": {…}}. The old run stays unchanged as history. If the run is not failed or cancelled, or another run of the group is active, you get 409; if nothing is left open, 400. Runs of other groups answer 404, and a viewer gets 403.

Examples

export LLARS=https://llars.example.org
export K=llars_…   # personal key with conference:write

# Your research groups
curl -s -H "X-API-Key: $K" "$LLARS/api/v1/conference-manager/research-groups"

# Create a series
curl -s -X POST -H "X-API-Key: $K" -H "Content-Type: application/json" \
  -d '{"group_id":1,"name":"Empirical Methods in NLP","acronym":"EMNLP","core_ranking":"A*"}' \
  "$LLARS/api/v1/conference-manager/series"

# Estimated edition (windows only)
curl -s -X POST -H "X-API-Key: $K" -H "Content-Type: application/json" \
  -d '{
    "group_id": 1, "series_id": 3, "name": "EMNLP 2027", "acronym": "EMNLP", "year": 2027,
    "submission_window_start": "2027-05-10", "submission_window_end": "2027-05-25",
    "notification_window_start": "2027-08-15", "notification_window_end": "2027-08-31",
    "conference_window_start": "2027-10-25", "conference_window_end": "2027-11-15",
    "estimation_basis": "derived from 2023-2026", "estimation_confidence": "medium"
  }' \
  "$LLARS/api/v1/conference-manager/conferences"

# The date is announced: set the exact value (the window stays as history)
curl -s -X PATCH -H "X-API-Key: $K" -H "Content-Type: application/json" \
  -d '{"submission_deadline":"2027-05-19T23:59:00"}' \
  "$LLARS/api/v1/conference-manager/conferences/12"

# All still-estimated editions from January 2027 on
curl -s -H "X-API-Key: $K" \
  "$LLARS/api/v1/conference-manager/conferences?group_id=1&estimated=true&from=2027-01-01"

# Paper with DOI, arXiv and authors
curl -s -X POST -H "X-API-Key: $K" -H "Content-Type: application/json" \
  -d '{
    "group_id": 1, "title": "Our Paper", "status": "published", "conference_id": 12,
    "doi": "doi:10.18653/v1/2024.acl-long.1", "arxiv_url": "arXiv:2401.12345",
    "authors": [{"username": "alice", "is_corresponding": true}, {"external_name": "Jane Doe"}]
  }' \
  "$LLARS/api/v1/conference-manager/papers"

# Append a submission
curl -s -X POST -H "X-API-Key: $K" -H "Content-Type: application/json" \
  -d '{"conference_id":12,"status":"submitted","submitted_at":"2027-05-19T20:00:00"}' \
  "$LLARS/api/v1/conference-manager/papers/5/submissions"

# Start, watch and cancel the AI refresh
curl -s -X POST -H "X-API-Key: $K" -H "Content-Type: application/json" \
  -d '{"group_id":1}' "$LLARS/api/v1/conference-manager/conferences/refresh"
curl -s -H "X-API-Key: $K" "$LLARS/api/v1/conference-manager/refresh-runs/7"
curl -s -X POST -H "X-API-Key: $K" "$LLARS/api/v1/conference-manager/refresh-runs/7/cancel"
# … and later resume the open editions in a new run
curl -s -X POST -H "X-API-Key: $K" "$LLARS/api/v1/conference-manager/refresh-runs/7/resume"

Security / hardening

  • IDOR masking (M1): objects of foreign groups answer with 404, not 403 — no ID-existence leaks. Cross references (series_id, conference_id) may not leave the group.
  • Scope ∧ permission: a key only works while its account holds the feature permission.
  • Strict schemas: unknown fields, invalid dates and non-http(s) links are a 400 instead of being dropped silently.
  • Size caps: keywords ≤ 50, authors ≤ 50, conference_ids ≤ 200, limit ≤ 200.
  • Conflicts (duplicates, active AI run) are 409 instead of 500.

Out of scope

  • Creating or changing research groups and memberships (UI only)
  • Moving records between groups
  • Deleting AI runs or scheduling them (the AI runs on request only)