# myCouncil API v1

Programmatic access to multi-LLM debate sessions. Run the same flow you see in
the web app — auto-configure a council, start a debate, poll for results,
export to PDF / TXT, and create public share links.

- **Base URL:** `https://app.mycouncil.xyz`
- **Auth:** `Authorization: Bearer mc_...` (your API key)
- **Quota:** every debate session deducts from your account quota. Standard
  auto-config is free; Advanced costs 1 round per call.

Generate a key in **Account → API**. The raw key is shown exactly once — save
it immediately, the server only stores a hash. You can have up to 5 active
keys at the same time.

---

## Quick start

```bash
KEY=mc_your_key_here
BASE=https://app.mycouncil.xyz

# 1) Check your remaining quota
curl -s -H "Authorization: Bearer $KEY" $BASE/api/v1/balance

# 2) Start a debate without picking a config — the server picks one for you
#    (free Standard auto-config under the hood)
curl -s -X POST -H "Authorization: Bearer $KEY" \
  -F "content=Top 3 takeaways from running microservices at scale" \
  $BASE/api/v1/debate
```

The response gives you a `job_id`. Poll it:

```bash
JOB=<job_id from previous response>

while :; do
  S=$(curl -s -H "Authorization: Bearer $KEY" $BASE/api/v1/jobs/$JOB | jq -r .status)
  echo "$S"
  [ "$S" = complete ] || [ "$S" = failed ] && break
  sleep 3
done
```

When `status=complete`, the same endpoint returns the full transcript
(`stage1`, `stage2`, `stage3`) and `llm_cost`.

---

## Endpoints

### GET /api/v1/balance

Your effective quota and current auto-config mode.

```bash
curl -s -H "Authorization: Bearer $KEY" $BASE/api/v1/balance
```

```json
{
  "questions_remaining": 10,
  "questions_total": 10,
  "autoconfig_mode": "standard"
}
```

---

### POST /api/v1/auto-config

Ask the planner LLM to design a session config from a user query.

- **Standard mode** (default, free): picks 2–5 roles from existing presets,
  uses a fast cheap model. ~2 seconds per call.
- **Advanced mode** (paid, 1 round): allows custom-tailored roles, uses a
  premium model. Deducted upfront; refunded automatically if the LLM call
  fails.

Mode is read from your account setting (`Account → Auto-config Settings`) —
you cannot override it per request.

```bash
curl -s -X POST -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
  -d '{"content":"Should we migrate from REST to GraphQL?"}' \
  $BASE/api/v1/auto-config
```

Response shape:

```json
{
  "config": { /* session config — pass straight to /debate */ },
  "observation": "Human-readable rationale in the same language as the query",
  "session_type": 1,
  "roles_summary": [
    {"role_name": "Tech Lead", "role_preset": "technology",
     "model_display_name": "GPT 5.2 Pro", "role_description": null}
  ],
  "chairman_display_name": "Claude Opus 4.6",
  "chairman_temperature": 0.4,
  "mode_used": "standard",
  "rounds_charged": 0,
  "questions_remaining": 10
}
```

If you're in **Advanced mode** with `questions_remaining < 1` the server
returns **403** — top up or switch to Standard in `Account → Auto-config
Settings`.

---

### POST /api/v1/debate

Run the full 3-stage debate. `multipart/form-data`:

| Field   | Type          | Required | Notes |
|---------|---------------|----------|-------|
| content | string        | yes      | User query |
| config  | string (JSON) | no       | Session config. If omitted, the server auto-configures inline using your current mode. |
| files   | binary[]      | no       | Optional file uploads (PDF / DOCX / TXT). OCR runs inside the job. |

**Without `config` (simplest):**

```bash
curl -s -X POST -H "Authorization: Bearer $KEY" \
  -F "content=Pros and cons of monorepos in 2026" \
  $BASE/api/v1/debate
```

**With your own `config`** (typically obtained from `/auto-config` first):

```bash
# 1) get a config
CFG=$(curl -s -X POST -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
  -d '{"content":"Migrate from REST to GraphQL?"}' $BASE/api/v1/auto-config \
  | jq -c '.config')

# 2) start the debate with that config
curl -s -X POST -H "Authorization: Bearer $KEY" \
  -F "content=Migrate from REST to GraphQL?" \
  -F "config=$CFG" \
  $BASE/api/v1/debate
```

**With files:**

```bash
curl -s -X POST -H "Authorization: Bearer $KEY" \
  -F "content=Summarize this document and propose 3 next steps" \
  -F "files=@/path/to/report.pdf" \
  -F "files=@/path/to/notes.docx" \
  $BASE/api/v1/debate
```

Response:

```json
{
  "job_id": "01aa9711-b1e1-4cfe-a5e2-cd83c3e19b93",
  "conversation_id": "a150982c-1e60-4b72-bf03-ab415a3bd8fb",
  "session_type": 1,
  "config_used": { /* fully resolved config the job is running on */ },
  "auto_config": {
    "mode_used": "standard",
    "rounds_charged": 0,
    "observation": "..."
  }
}
```

`auto_config` is only present when the server generated the config (i.e. you
didn't pass `config` yourself).

**Quota:**

- Three-stage council (`session_type=1`): **1 round** deducted on start.
- Adversarial debate (`session_type=2`): up to `adf_settings.max_rounds`
  rounds. The server pre-checks you have enough balance before starting;
  unused rounds are refunded after the debate finishes.

---

### GET /api/v1/jobs/{job_id}

Poll a running or completed debate.

```bash
curl -s -H "Authorization: Bearer $KEY" $BASE/api/v1/jobs/$JOB
```

Key fields:

| Field       | Description |
|-------------|-------------|
| `status`    | `ocr_in_progress` / `stage1_in_progress` / `stage2_step1_in_progress` / `stage2_step2_in_progress` / `aggregation_in_progress` / `stage3_in_progress` / `adf_*` / `complete` / `failed` |
| `progress`  | 0-100 percent |
| `stage1`    | List of expert responses |
| `stage2`    | Peer reviews + label-to-model map |
| `stage3`    | Chairman synthesis (executive_summary, final_recommendation, consensus_points, controversies, …) |
| `metadata.meta_statistics` | Preference matrix, consensus strength, key insights |
| `llm_cost`  | Total LLM dollar cost (only when complete) |
| `error`     | Failure reason (only when `status=failed`) |

---

### Conversation utilities

These predate the `/api/v1/*` namespace but accept your API key just the
same. Use the `conversation_id` from the `/debate` response.

#### Public share link

```bash
# enable
curl -s -X POST -H "Authorization: Bearer $KEY" \
  $BASE/api/conversations/$CONV/share
# -> { "share_url": "https://app.mycouncil.xyz/s/<id>", "is_public": true }

# disable
curl -s -X DELETE -H "Authorization: Bearer $KEY" \
  $BASE/api/conversations/$CONV/share
```

#### PDF / TXT export

```bash
curl -s -H "Authorization: Bearer $KEY" \
  $BASE/api/conversations/$CONV/export/pdf -o session.pdf

curl -s -H "Authorization: Bearer $KEY" \
  $BASE/api/conversations/$CONV/export/txt -o session.txt
```

You can only share / export your own sessions.

---

## Team members (owner only)

If your account has invited team members, these endpoints let you inspect
their usage and manage API keys on their behalf. They reject members and
respond 403 if called from a member account.

| Method | Path | Purpose |
|--------|------|---------|
| GET    | `/api/v1/members`                              | List invites + active members with usage summaries |
| GET    | `/api/v1/members/{member_id}`                  | One active member's card |
| GET    | `/api/v1/members/{member_id}/api-keys`         | List the member's active API keys |
| POST   | `/api/v1/members/{member_id}/api-keys`         | Issue a key on the member's behalf. Returns `raw_key` exactly once. |
| DELETE | `/api/v1/members/{member_id}/api-keys/{key_id}`| Soft-revoke the member's key |

Each active-member entry includes a `usage` block computed from the round
ledger:

```json
{
  "rounds_used_total": 8,
  "rounds_used_this_month": 3,
  "sessions_count_total": 2,
  "sessions_count_this_month": 1,
  "last_active_at": "2026-05-20T21:55:05",
  "api_keys_count": 1
}
```

`rounds_used_*` is the accurate spend (matters for adversarial debates where
one session may cost multiple rounds). `questions_used` is a legacy alias
for `sessions_count_total`.

```bash
MEMBER=<member_id from /api/v1/members>

# Issue a key — copy raw_key now, it's only shown once
curl -s -X POST -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
  -d '{"name":"ci-bot"}' \
  $BASE/api/v1/members/$MEMBER/api-keys

# Revoke when it rotates
curl -s -X DELETE -H "Authorization: Bearer $KEY" \
  $BASE/api/v1/members/$MEMBER/api-keys/$KEY_ID
```

**Auth invariant:** these owner-on-behalf endpoints accept your API key, but
the personal `POST /api/api-keys` stays JWT-only — an owner's API key
**cannot** mint more owner-level keys for itself. A leaked owner key can
only create child-member keys, which are immediately listable and revokable.

Members and child API keys draw rounds from the owner's pool — there is no
separate per-member quota.

---

## Errors

| HTTP | Meaning |
|------|---------|
| 401  | Missing or invalid API key (revoked, deleted, or wrong) |
| 403  | Quota exceeded, or trying to manage API keys through an API key (use the web UI for key management) |
| 404  | Job / conversation does not exist or belongs to another user |
| 422  | Validation error in request body / multipart fields |
| 500  | LLM provider or internal failure. Advanced auto-config quota is refunded automatically on this path. |

Error body is always JSON:

```json
{"detail": "human-readable message"}
```

---

## Quota & cost reference

- Free tier: 10 rounds per account on signup.
- `/api/v1/balance` is the source of truth.
- Standard auto-config is free; Advanced auto-config costs 1 round per call.
- Every `/debate` start deducts 1 round upfront; adversarial debates deduct
  remaining rounds after completion (and refund unused ones).
- `llm_cost` on a finished job is the underlying LLM provider cost in USD —
  separate from your round-based quota. It's exposed for transparency, not
  billing.

Per-key usage statistics (round consumption + last-used timestamp) are
visible in the web UI under **Account → API**.
