{
  "openapi": "3.1.0",
  "info": {
    "title": "Decidi API",
    "version": "1.0.0",
    "summary": "Run a council of frontier AI models on a decision, and get one audited verdict.",
    "description": "Submit a decision or a piece of work; several frontier AI models (GPT, Claude, Gemini, Grok) plus optional expert personas debate it over N rounds, a moderator synthesises a verdict, and an always-on Final QA audit reviews it for hallucinations and weak reasoning. You get one signed-off verdict — billed in credits against real model usage. Built for AI agents (Claude Code, Cursor, and others) that want to run a high-stakes decision past a multi-model council before acting, as well as for direct programmatic use.",
    "contact": {
      "name": "Decidi support",
      "email": "support@decidi.ai",
      "url": "https://decidi.ai/docs"
    },
    "termsOfService": "https://decidi.ai/terms"
  },
  "servers": [
    {
      "url": "https://decidi.ai",
      "description": "Production"
    }
  ],
  "externalDocs": {
    "description": "Human docs + agent guide",
    "url": "https://decidi.ai/docs"
  },
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "paths": {
    "/api/v1/council": {
      "post": {
        "operationId": "runCouncil",
        "summary": "Convene a council on a decision or piece of work",
        "description": "Several frontier models (and optional expert personas) debate the submitted question/work over N rounds, a moderator synthesises a verdict, and a Final QA audit reviews it. Charges credits based on real model usage; any credits held above the real cost are refunded. Long runs are supported (up to ~13 minutes) — use a generous client timeout (>= 120s).",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CouncilRequest"
              },
              "example": {
                "question": "Should we raise our SaaS prices 20% now, or hold and grow volume first?",
                "level": "standard",
                "rounds": 2,
                "personaIds": [
                  "cfo",
                  "devils-advocate",
                  "strategy-consultant"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The council's verdict.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CouncilResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request — invalid JSON (`bad_json`) or a question under 5 characters (`missing_question`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing (`missing_api_key`) or invalid (`invalid_api_key`) API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "Insufficient credits (`insufficient_credits`) — the body includes `need` and `have`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited (`rate_limited`) — 60 requests per hour per account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Server error — `account_unavailable` or `debate_failed`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "504": {
            "description": "Run too large to finish in the time available (`run_too_large`) — every held credit is refunded; retry with fewer members, a lower depth, or a narrower brief.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "dck_live_…",
        "description": "A Decidi API key, created at https://decidi.ai/account. Send it as `Authorization: Bearer dck_live_…`. Usage bills the key owner's credit balance."
      }
    },
    "schemas": {
      "CouncilRequest": {
        "type": "object",
        "required": [
          "question"
        ],
        "properties": {
          "question": {
            "type": "string",
            "minLength": 5,
            "description": "The decision, question, or work to review. For a review, paste the actual work (a contract, a plan, code, a pitch)."
          },
          "level": {
            "type": "string",
            "enum": [
              "quick",
              "standard",
              "deep"
            ],
            "default": "standard",
            "description": "Council depth. quick ≈ a minute; standard ≈ 2–3 minutes; deep ≈ 3–5 minutes with heavyweight reasoning models. Deeper costs more credits. (The raw ids fast/normal/max are also accepted.)"
          },
          "rounds": {
            "type": "integer",
            "minimum": 1,
            "maximum": 4,
            "default": 2,
            "description": "Debate rounds — how many times members challenge each other before the verdict."
          },
          "personaIds": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "minItems": 2,
            "maxItems": 6,
            "example": [
              "cfo",
              "devils-advocate",
              "strategy-consultant"
            ],
            "description": "Which expert personas to seat (2–6). Omit for a balanced default council (devils-advocate, pragmatist, first-principles, strategy-consultant). See https://decidi.ai/personas for all persona ids; you may repeat an id to seat the same mind more than once."
          },
          "intent": {
            "type": "string",
            "description": "Optional hint about the kind of review (e.g. 'contract-review', 'code-review') to sharpen the council and the deliverable."
          },
          "attachments": {
            "type": "array",
            "maxItems": 6,
            "description": "Optional context for the council — up to 6 attachments. Documents: {name, type, text} with the extracted text. Images: {name, type, dataUrl} with a data: URL the vision models can see.",
            "items": {
              "oneOf": [
                {
                  "type": "object",
                  "required": [
                    "name",
                    "text"
                  ],
                  "properties": {
                    "name": {
                      "type": "string",
                      "description": "File name, e.g. contract.docx"
                    },
                    "type": {
                      "type": "string",
                      "description": "MIME type or file kind, e.g. application/pdf"
                    },
                    "text": {
                      "type": "string",
                      "description": "The document text content"
                    }
                  },
                  "additionalProperties": false
                },
                {
                  "type": "object",
                  "required": [
                    "name",
                    "dataUrl"
                  ],
                  "properties": {
                    "name": {
                      "type": "string",
                      "description": "File name, e.g. screenshot.png"
                    },
                    "type": {
                      "type": "string",
                      "description": "Image MIME type, e.g. image/png"
                    },
                    "dataUrl": {
                      "type": "string",
                      "description": "data: URL of the image"
                    }
                  },
                  "additionalProperties": false
                }
              ]
            }
          }
        }
      },
      "CouncilResponse": {
        "type": "object",
        "properties": {
          "verdict": {
            "type": "string",
            "description": "The QA-signed-off verdict — the decisive answer with reasoning, risks, and next steps."
          },
          "draft": {
            "type": "string",
            "description": "The moderator's synthesis, before the Final QA audit."
          },
          "qa": {
            "type": [
              "string",
              "null"
            ],
            "description": "The Final QA review, including any 'verify before you rely on this' items surfaced."
          },
          "artifact_html": {
            "type": [
              "string",
              "null"
            ],
            "description": "A clean, on-brand HTML deliverable when one was produced for this ask, otherwise null."
          },
          "models": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "The frontier models that contributed."
          },
          "synth_model": {
            "type": "string",
            "description": "The model used for the moderator synthesis."
          },
          "qa_model": {
            "type": "string",
            "description": "The model used for the Final QA audit."
          },
          "credits_charged": {
            "type": "integer",
            "description": "Credits actually charged (real usage; the held remainder is refunded to your balance)."
          },
          "balance": {
            "type": "integer",
            "description": "Your credit balance after this run."
          },
          "usd_cost": {
            "type": "number",
            "description": "The underlying model cost in USD for this run."
          },
          "level": {
            "type": "string",
            "description": "The depth the run used (fast/normal/max)."
          },
          "personas": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "The persona ids that were seated."
          },
          "rounds": {
            "type": "integer"
          }
        }
      },
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "string",
            "description": "A stable, machine-readable error code (e.g. invalid_api_key, insufficient_credits, rate_limited, run_too_large)."
          },
          "message": {
            "type": "string"
          },
          "hint": {
            "type": "string"
          },
          "need": {
            "type": "integer",
            "description": "On insufficient_credits: credits required."
          },
          "have": {
            "type": "integer",
            "description": "On insufficient_credits: credits available."
          }
        }
      }
    }
  }
}
