{
  "openapi": "3.0.3",
  "info": {
    "title": "DeckForger API",
    "version": "1.0.0",
    "description": "Analyze Magic: The Gathering decklists with Scryfall-backed card data when available, and explicit structural fallback diagnostics when card data cannot be resolved."
  },
  "servers": [
    {
      "url": "https://deckforger.com"
    }
  ],
  "paths": {
    "/api/analyze": {
      "get": {
        "summary": "Describe DeckForger API capabilities",
        "operationId": "getAnalyzeCapabilities",
        "responses": {
          "200": {
            "description": "Machine-readable API discovery response for clients that can only issue GET requests.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CapabilityResponse"
                },
                "examples": {
                  "capabilities": {
                    "value": {
                      "tool": "DeckForger",
                      "deckforger_version": "1.0.0",
                      "status": "ok",
                      "message": "DeckForger API is live. Use POST /api/analyze to analyze a submitted MTG decklist.",
                      "capabilities": [
                        "MTG decklist parsing",
                        "Scryfall-enriched card resolution",
                        "Standard and Commander legality review",
                        "mana curve analysis",
                        "mana-base source diagnostics",
                        "archetype detection",
                        "score breakdown",
                        "structured warnings",
                        "strengths, weaknesses, and recommendations"
                      ],
                      "endpoints": {
                        "analyze": {
                          "method": "POST",
                          "path": "/api/analyze",
                          "content_type": "application/json"
                        },
                        "demo": {
                          "method": "GET",
                          "path": "/api/analyze/demo"
                        },
                        "openapi": {
                          "method": "GET",
                          "path": "/openapi.json"
                        },
                        "llms": {
                          "method": "GET",
                          "path": "/llms.txt"
                        }
                      },
                      "example_request": {
                        "decklist": "Deck\n4 Gingerbrute\n4 Chainsaw\n22 Mountain",
                        "format": "standard",
                        "best_of": "bo1"
                      },
                      "commander_example_request": {
                        "decklist": "Commander\n1 Tersa Lightshatter\n\nDeck\n35 Mountain\n1 Lightning Strike",
                        "format": "commander"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "summary": "Analyze an MTG decklist",
        "operationId": "analyzeDeck",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AnalyzeRequest"
              },
              "examples": {
                "minimalArtifactAggro": {
                  "summary": "Minimal incomplete artifact aggro deck",
                  "value": {
                    "decklist": "Deck\n4 Gingerbrute\n4 Chainsaw\n22 Mountain",
                    "format": "standard",
                    "best_of": "bo1"
                  }
                },
                "commanderAnalyzeOnly": {
                  "summary": "Commander analyze-only deck",
                  "value": {
                    "decklist": "Commander\n1 Tersa Lightshatter\n\nDeck\n35 Mountain\n1 Lightning Strike",
                    "format": "commander"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Deck analysis result. Unknown card names and Scryfall misses are returned as review diagnostics, not server errors.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeckAnalysis"
                },
                "examples": {
                  "scryfallEnriched": {
                    "summary": "Scryfall-enriched response",
                    "value": {
                      "tool": "DeckForger",
                      "deckforger_version": "1.0.0",
                      "analyzer_source": "site_app_analyzer",
                      "analysis_mode": "scryfall_enriched_api",
                      "format": "standard",
                      "best_of": "bo1",
                      "deck_size": 30,
                      "archetype": "artifact aggro",
                      "score": 4,
                      "score_breakdown": {
                        "overall_raw": 5.4,
                        "structural_raw": 5.6,
                        "cap": 4,
                        "mana_curve": 8,
                        "land_count": 9,
                        "color_consistency": 9,
                        "early_game": 7.8,
                        "threat_density": 2.5,
                        "interaction_removal": 5,
                        "card_advantage": 3.5,
                        "synergy_density": 6.4,
                        "win_condition_clarity": 0
                      },
                      "confidence": 0.67,
                      "summary": "artifact aggro Scryfall-enriched audit with 22 lands, 0 interaction slots, 0 card-advantage slots, 0 selection slots, and 4 repeatable-value slots.",
                      "card_data": {
                        "source": "Scryfall",
                        "mode": "cache",
                        "checked_at": "2026-06-29T00:00:00.000Z",
                        "resolved_unique_cards": 3,
                        "resolved_card_copies": 30,
                        "unresolved_unique_cards": 0,
                        "unresolved_card_copies": 0,
                        "unresolved_names": []
                      },
                      "facts": {
                        "lands": 22,
                        "nonlands": 8,
                        "colors": ["R"],
                        "artifact_count": 8,
                        "creature_count": 4,
                        "interaction_count": 0,
                        "card_advantage_count": 0,
                        "selection_count": 0,
                        "recursion_count": 0,
                        "repeatable_value_count": 4,
                        "enchantment_count": 0,
                        "equipment_count": 4,
                        "vehicle_count": 0
                      },
                      "legality": {
                        "status": "illegal",
                        "main_deck_size": 30,
                        "sideboard_size": 0,
                        "format": "standard",
                        "illegal_cards": [],
                        "unknown_cards": [],
                        "notes": [
                          "Standard main decks should contain at least 60 cards; this list is flagged for review."
                        ]
                      },
                      "mana_curve": {
                        "0": 0,
                        "1": 4,
                        "2": 4,
                        "3": 0,
                        "4": 0,
                        "5+": 0,
                        "unknown": 0
                      },
                      "mana_base": {
                        "land_count": 22,
                        "land_count_status": "normal",
                        "color_sources": {
                          "W": 0,
                          "U": 0,
                          "B": 0,
                          "R": 22,
                          "G": 0
                        },
                        "early_color_requirements": {
                          "W": 0,
                          "U": 0,
                          "B": 0,
                          "R": 4,
                          "G": 0
                        },
                        "color_consistency_status": "ok",
                        "notes": []
                      },
                      "warnings": [
                        {
                          "severity": "critical",
                          "code": "MAIN_DECK_TOO_SMALL",
                          "message": "Standard main decks must contain at least 60 cards."
                        }
                      ],
                      "strengths": [],
                      "weaknesses": [
                        "Main deck is below 60 cards.",
                        "Land count cannot be meaningfully judged until the main deck reaches 60 cards.",
                        "Early curve may be light.",
                        "Interaction density may be low."
                      ],
                      "recommendations": [
                        "Add more one- and two-mana plays if the deck is losing before stabilizing."
                      ]
                    }
                  },
                  "fallback": {
                    "summary": "Structural fallback response",
                    "value": {
                      "tool": "DeckForger",
                      "deckforger_version": "1.0.0",
                      "analyzer_source": "site_app_analyzer",
                      "analysis_mode": "structural_api",
                      "format": "standard",
                      "best_of": "bo1",
                      "deck_size": 4,
                      "archetype": "unclassified deck",
                      "score": 1.2,
                      "confidence": 0.28,
                      "summary": "unclassified deck structural audit with 0 lands, 0 interaction slots, 0 card-advantage slots, 0 selection slots, and 0 repeatable-value slots.",
                      "card_data": {
                        "source": "Scryfall",
                        "mode": "fallback",
                        "checked_at": "2026-06-29T00:00:00.000Z",
                        "resolved_unique_cards": 0,
                        "resolved_card_copies": 0,
                        "unresolved_unique_cards": 1,
                        "unresolved_card_copies": 4,
                        "unresolved_names": ["Totally Fake Card"]
                      },
                      "facts": {
                        "lands": 0,
                        "nonlands": 4,
                        "colors": [],
                        "artifact_count": 0,
                        "creature_count": 0,
                        "interaction_count": 0,
                        "card_advantage_count": 0,
                        "selection_count": 0,
                        "recursion_count": 0,
                        "repeatable_value_count": 0,
                        "enchantment_count": 0,
                        "equipment_count": 0,
                        "vehicle_count": 0
                      },
                      "legality": {
                        "status": "illegal",
                        "main_deck_size": 4,
                        "sideboard_size": 0,
                        "format": "standard",
                        "illegal_cards": [],
                        "unknown_cards": ["Totally Fake Card"],
                        "notes": [
                          "Some card names could not be resolved from Scryfall-backed data.",
                          "Scryfall lookup failed; DeckForger returned structural fallback analysis."
                        ]
                      },
                      "mana_curve": {
                        "0": 0,
                        "1": 0,
                        "2": 0,
                        "3": 0,
                        "4": 0,
                        "5+": 0,
                        "unknown": 4
                      },
                      "warnings": [
                        {
                          "severity": "critical",
                          "code": "MAIN_DECK_TOO_SMALL",
                          "message": "Standard main decks must contain at least 60 cards."
                        },
                        {
                          "severity": "major",
                          "code": "UNRESOLVED_CARD_NAMES",
                          "message": "1 unique card name could not be resolved from Scryfall-backed data."
                        },
                        {
                          "severity": "major",
                          "code": "SCRYFALL_UNAVAILABLE",
                          "message": "Scryfall lookup failed, so DeckForger returned structural fallback analysis."
                        }
                      ],
                      "strengths": [],
                      "weaknesses": [
                        "Main deck is below 60 cards.",
                        "Land count cannot be meaningfully judged until the main deck reaches 60 cards."
                      ],
                      "recommendations": []
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request, invalid JSON, empty decklist, missing decklist, or unsupported format.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "examples": {
                  "missingDecklist": {
                    "value": {
                      "tool": "DeckForger",
                      "deckforger_version": "1.0.0",
                      "error": {
                        "code": "missing_decklist",
                        "message": "decklist is required."
                      }
                    }
                  }
                }
              }
            }
          },
          "405": {
            "description": "Unsupported method.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error returned as JSON.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/analyze/demo": {
      "get": {
        "summary": "Return a fixed public demo deck analysis",
        "operationId": "getAnalyzeDemo",
        "responses": {
          "200": {
            "description": "A real DeckForger analysis for a fixed public demo deck. This endpoint is useful for crawlers, health checks, and agents that can only issue GET requests.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeckAnalysis"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "CapabilityResponse": {
        "type": "object",
        "required": ["tool", "deckforger_version", "status", "message", "capabilities", "endpoints", "example_request"],
        "properties": {
          "tool": { "type": "string", "enum": ["DeckForger"] },
          "deckforger_version": { "type": "string" },
          "status": { "type": "string", "enum": ["ok"] },
          "message": { "type": "string" },
          "capabilities": {
            "type": "array",
            "items": { "type": "string" }
          },
          "endpoints": {
            "type": "object",
            "properties": {
              "analyze": { "$ref": "#/components/schemas/EndpointInfo" },
              "demo": { "$ref": "#/components/schemas/EndpointInfo" },
              "openapi": { "$ref": "#/components/schemas/EndpointInfo" },
              "llms": { "$ref": "#/components/schemas/EndpointInfo" }
            }
          },
          "example_request": { "$ref": "#/components/schemas/AnalyzeRequest" },
          "commander_example_request": { "$ref": "#/components/schemas/AnalyzeRequest" }
        }
      },
      "EndpointInfo": {
        "type": "object",
        "required": ["method", "path"],
        "properties": {
          "method": { "type": "string", "enum": ["GET", "POST"] },
          "path": { "type": "string" },
          "content_type": { "type": "string" }
        }
      },
      "AnalyzeRequest": {
        "type": "object",
        "required": ["decklist"],
        "properties": {
          "decklist": {
            "type": "string",
            "description": "A pasted Magic: The Gathering decklist in Arena-style or plain quantity/name format.",
            "minLength": 1
          },
          "format": {
            "type": "string",
            "default": "standard",
            "description": "Requested legality format.",
            "enum": ["standard", "alchemy", "historic", "timeless", "pioneer", "modern", "legacy", "vintage", "commander", "brawl", "standardbrawl", "pauper"]
          },
          "best_of": {
            "type": "string",
            "default": "bo1",
            "enum": ["bo1", "bo3"],
            "description": "Whether the deck is intended for best-of-one or best-of-three play."
          }
        },
        "additionalProperties": true
      },
      "DeckAnalysis": {
        "type": "object",
        "required": ["tool", "deckforger_version", "analyzer_source", "analysis_mode", "format", "best_of", "deck_size", "card_data", "facts", "legality", "mana_curve", "mana_base", "warnings"],
        "properties": {
          "tool": { "type": "string", "enum": ["DeckForger"] },
          "deckforger_version": { "type": "string", "example": "1.0.0" },
          "analyzer_source": { "type": "string", "enum": ["site_app_analyzer"] },
          "analysis_mode": {
            "type": "string",
            "enum": ["scryfall_enriched_api", "structural_api"]
          },
          "format": { "type": "string" },
          "best_of": { "type": "string", "enum": ["bo1", "bo3"] },
          "deck_size": { "type": "integer", "minimum": 0 },
          "commander": { "type": "string", "nullable": true },
          "commanders": {
            "type": "array",
            "items": { "type": "string" }
          },
          "archetype": { "type": "string" },
          "score": { "type": "number", "minimum": 1, "maximum": 10 },
          "score_breakdown": { "$ref": "#/components/schemas/ScoreBreakdown" },
          "site_scores": {
            "type": "object",
            "description": "Raw score object from the shared DeckForger site analyzer."
          },
          "confidence": { "type": "number", "minimum": 0, "maximum": 1 },
          "confidence_reason": { "type": "string" },
          "summary": { "type": "string" },
          "card_data": { "$ref": "#/components/schemas/CardDataDiagnostics" },
          "facts": { "$ref": "#/components/schemas/DeckFacts" },
          "legality": { "$ref": "#/components/schemas/Legality" },
          "mana_curve": { "$ref": "#/components/schemas/ManaCurve" },
          "mana_base": { "$ref": "#/components/schemas/ManaBase" },
          "warnings": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/AnalysisWarning" }
          },
          "strengths": {
            "type": "array",
            "items": { "type": "string" }
          },
          "weaknesses": {
            "type": "array",
            "items": { "type": "string" }
          },
          "recommendations": {
            "type": "array",
            "items": { "type": "string" }
          }
        }
      },
      "CardDataDiagnostics": {
        "type": "object",
        "required": ["source", "mode", "checked_at", "resolved_unique_cards", "resolved_card_copies", "unresolved_unique_cards", "unresolved_card_copies", "unresolved_names"],
        "properties": {
          "source": { "type": "string", "example": "Scryfall" },
          "mode": { "type": "string", "enum": ["live", "cache", "fallback"] },
          "checked_at": { "type": "string", "format": "date-time" },
          "resolved_unique_cards": { "type": "integer", "minimum": 0 },
          "resolved_card_copies": { "type": "integer", "minimum": 0 },
          "unresolved_unique_cards": { "type": "integer", "minimum": 0 },
          "unresolved_card_copies": { "type": "integer", "minimum": 0 },
          "unresolved_names": {
            "type": "array",
            "items": { "type": "string" }
          }
        }
      },
      "ScoreBreakdown": {
        "type": "object",
        "required": ["overall_raw", "structural_raw", "cap", "mana_curve", "land_count", "color_consistency", "early_game", "threat_density", "interaction_removal", "card_advantage", "synergy_density", "win_condition_clarity"],
        "properties": {
          "overall_raw": { "type": "number" },
          "structural_raw": { "type": "number" },
          "cap": { "type": "number" },
          "mana_curve": { "type": "number" },
          "land_count": { "type": "number" },
          "color_consistency": { "type": "number" },
          "early_game": { "type": "number" },
          "threat_density": { "type": "number" },
          "threat_velocity": { "type": "number" },
          "payoff_reliability": { "type": "number" },
          "interaction_removal": { "type": "number" },
          "card_advantage": { "type": "number" },
          "synergy_density": { "type": "number" },
          "synergy_velocity": { "type": "number" },
          "redundancy": { "type": "number" },
          "resilience": { "type": "number" },
          "win_condition_clarity": { "type": "number" },
          "bo1_suitability": { "type": "number" },
          "wildcard_efficiency": { "type": "number" }
        }
      },
      "AnalysisWarning": {
        "type": "object",
        "required": ["severity", "code", "message"],
        "properties": {
          "severity": { "type": "string", "enum": ["minor", "major", "critical"] },
          "code": { "type": "string" },
          "message": { "type": "string" }
        }
      },
      "DeckFacts": {
        "type": "object",
        "required": ["lands", "nonlands", "colors", "artifact_count", "creature_count", "interaction_count", "card_advantage_count"],
        "properties": {
          "lands": { "type": "integer", "minimum": 0 },
          "nonlands": { "type": "integer", "minimum": 0 },
          "colors": {
            "type": "array",
            "items": { "type": "string", "enum": ["W", "U", "B", "R", "G"] }
          },
          "commander": { "type": "string", "nullable": true },
          "commanders": {
            "type": "array",
            "items": { "type": "string" }
          },
          "commander_colors": {
            "type": "array",
            "items": { "type": "string", "enum": ["W", "U", "B", "R", "G"] }
          },
          "artifact_count": { "type": "integer", "minimum": 0 },
          "creature_count": { "type": "integer", "minimum": 0 },
          "interaction_count": { "type": "integer", "minimum": 0 },
          "card_advantage_count": { "type": "integer", "minimum": 0 },
          "selection_count": { "type": "integer", "minimum": 0 },
          "recursion_count": { "type": "integer", "minimum": 0 },
          "repeatable_value_count": { "type": "integer", "minimum": 0 },
          "enchantment_count": { "type": "integer", "minimum": 0 },
          "equipment_count": { "type": "integer", "minimum": 0 },
          "vehicle_count": { "type": "integer", "minimum": 0 }
        }
      },
      "Legality": {
        "type": "object",
        "required": ["status", "main_deck_size", "sideboard_size", "format", "illegal_cards", "unknown_cards", "notes"],
        "properties": {
          "status": { "type": "string", "enum": ["legal", "unknown", "illegal"] },
          "main_deck_size": { "type": "integer", "minimum": 0 },
          "sideboard_size": { "type": "integer", "minimum": 0 },
          "format": { "type": "string" },
          "illegal_cards": {
            "type": "array",
            "items": { "type": "string" }
          },
          "unknown_cards": {
            "type": "array",
            "items": { "type": "string" }
          },
          "copy_limit_cards": {
            "type": "array",
            "items": {
              "oneOf": [
                { "type": "string" },
                { "type": "object" }
              ]
            }
          },
          "commander_present": { "type": "boolean" },
          "commander_resolved": { "type": "boolean" },
          "commander_eligible": { "type": "boolean" },
          "commander_color_identity_legal": { "type": "boolean" },
          "color_identity_violations": {
            "type": "array",
            "items": { "type": "object" }
          },
          "notes": {
            "type": "array",
            "items": { "type": "string" }
          }
        }
      },
      "ManaCurve": {
        "type": "object",
        "required": ["0", "1", "2", "3", "4", "5+", "unknown"],
        "properties": {
          "0": { "type": "integer", "minimum": 0 },
          "1": { "type": "integer", "minimum": 0 },
          "2": { "type": "integer", "minimum": 0 },
          "3": { "type": "integer", "minimum": 0 },
          "4": { "type": "integer", "minimum": 0 },
          "5+": { "type": "integer", "minimum": 0 },
          "unknown": { "type": "integer", "minimum": 0 }
        },
        "additionalProperties": false
      },
      "ManaBase": {
        "type": "object",
        "required": ["land_count", "land_count_status", "color_sources", "early_color_requirements", "color_consistency_status", "notes"],
        "properties": {
          "land_count": { "type": "integer", "minimum": 0 },
          "land_count_status": {
            "type": "string",
            "enum": ["low", "normal", "high", "acceptable_for_low_curve_aggro"]
          },
          "color_sources": { "$ref": "#/components/schemas/ColorCounts" },
          "early_color_requirements": { "$ref": "#/components/schemas/ColorCounts" },
          "color_consistency_status": {
            "type": "string",
            "enum": ["ok", "review"]
          },
          "notes": {
            "type": "array",
            "items": { "type": "string" }
          }
        }
      },
      "ColorCounts": {
        "type": "object",
        "required": ["W", "U", "B", "R", "G"],
        "properties": {
          "W": { "type": "integer", "minimum": 0 },
          "U": { "type": "integer", "minimum": 0 },
          "B": { "type": "integer", "minimum": 0 },
          "R": { "type": "integer", "minimum": 0 },
          "G": { "type": "integer", "minimum": 0 }
        },
        "additionalProperties": false
      },
      "ErrorResponse": {
        "type": "object",
        "required": ["tool", "deckforger_version", "error"],
        "properties": {
          "tool": { "type": "string", "enum": ["DeckForger"] },
          "deckforger_version": { "type": "string" },
          "error": {
            "type": "object",
            "required": ["code", "message"],
            "properties": {
              "code": {
                "type": "string",
                "enum": ["invalid_json", "missing_decklist", "empty_decklist", "unsupported_format", "method_not_allowed", "internal_error"]
              },
              "message": { "type": "string" },
              "supported_formats": {
                "type": "array",
                "items": { "type": "string" }
              },
              "detail": { "type": "string" }
            },
            "additionalProperties": true
          }
        }
      }
    }
  }
}
