{
  "openapi": "3.1.0",
  "info": {
    "title": "Econ Graph Studio API",
    "version": "1.0.0",
    "description": "Create IB Economics diagrams from JSON. Guide: https://graphs.lectere.com/llms-full.txt"
  },
  "servers": [
    {
      "url": "https://graphs.lectere.com"
    }
  ],
  "paths": {
    "/api/v1/templates": {
      "get": {
        "operationId": "listTemplates",
        "summary": "List diagram templates",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Search words"
          },
          {
            "name": "unit",
            "in": "query",
            "schema": {
              "enum": [
                "intro",
                "micro",
                "macro",
                "global",
                "custom"
              ]
            }
          },
          {
            "name": "hl",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Templates",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/templates/{id}": {
      "get": {
        "operationId": "getTemplate",
        "summary": "A template's params, panels and label keys",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "ppc-basic",
                "ppc-shift",
                "ppc-opportunity-cost",
                "ppc-actual-potential-growth",
                "demand-curve",
                "demand-shift",
                "supply-curve",
                "supply-shift",
                "market-equilibrium",
                "demand-change-equilibrium",
                "supply-change-equilibrium",
                "demand-supply-both-shift",
                "excess-demand-supply",
                "consumer-producer-surplus",
                "allocative-efficiency",
                "ped-types",
                "ped-linear-demand",
                "ped-total-revenue",
                "pes-types",
                "commodity-price-volatility",
                "indirect-tax-specific",
                "indirect-tax-ad-valorem",
                "tax-incidence-elasticity",
                "subsidy",
                "price-ceiling",
                "price-floor",
                "minimum-wage",
                "linear-demand-supply",
                "negative-production-externality",
                "negative-consumption-externality",
                "positive-production-externality",
                "positive-consumption-externality",
                "tradable-permits",
                "product-curves",
                "short-run-costs",
                "long-run-average-cost",
                "revenue-perfect-competition",
                "revenue-imperfect-competition",
                "profit-maximisation",
                "perfect-competition-short-run",
                "perfect-competition-long-run",
                "shut-down-break-even",
                "monopoly-profit",
                "monopoly-welfare-loss",
                "monopoly-revenue-maximisation",
                "natural-monopoly",
                "monopolistic-competition-short-run",
                "monopolistic-competition-long-run",
                "kinked-demand",
                "price-discrimination",
                "business-cycle",
                "ad-curve",
                "sras-shift",
                "ad-as-long-run-equilibrium",
                "keynesian-as",
                "keynesian-ad-increase",
                "recessionary-gap-nc",
                "inflationary-gap-nc",
                "recessionary-gap-keynesian",
                "inflationary-gap-keynesian",
                "demand-pull-inflation",
                "cost-push-inflation",
                "deflation",
                "natural-rate-unemployment",
                "demand-deficient-unemployment",
                "real-wage-unemployment",
                "structural-unemployment",
                "short-run-phillips-curve",
                "long-run-phillips-curve",
                "money-market",
                "ad-policy-shift",
                "keynesian-multiplier",
                "crowding-out",
                "economic-growth-adas",
                "supply-side-policy",
                "lorenz-curve",
                "free-trade-importing",
                "free-trade-exporting",
                "comparative-advantage",
                "tariff",
                "import-quota",
                "production-subsidy-trade",
                "export-subsidy",
                "floating-exchange-rate",
                "fixed-exchange-rate",
                "managed-exchange-rate",
                "j-curve",
                "blank-axes"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Template schema",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Invalid spec",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown template",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/render": {
      "post": {
        "operationId": "renderGraph",
        "summary": "Render a graph spec to SVG + edit link",
        "parameters": [
          {
            "name": "format",
            "in": "query",
            "schema": {
              "enum": [
                "json",
                "svg"
              ],
              "default": "json"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GraphSpec"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Rendered graph",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RenderResult"
                }
              },
              "image/svg+xml": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "description": "Invalid spec",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown template",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/render.svg": {
      "get": {
        "operationId": "renderGraphSvgFromSpec",
        "summary": "Render a spec passed in the URL",
        "parameters": [
          {
            "name": "spec",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "URL-encoded JSON, base64url JSON, or the value from svgUrl"
          }
        ],
        "responses": {
          "200": {
            "description": "SVG image",
            "content": {
              "image/svg+xml": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "description": "Invalid spec",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown template",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/render/{id}.svg": {
      "get": {
        "operationId": "renderGraphSvgFromQuery",
        "summary": "Render from query params (title, style.*, label.*, hide, and template params)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "ppc-basic",
                "ppc-shift",
                "ppc-opportunity-cost",
                "ppc-actual-potential-growth",
                "demand-curve",
                "demand-shift",
                "supply-curve",
                "supply-shift",
                "market-equilibrium",
                "demand-change-equilibrium",
                "supply-change-equilibrium",
                "demand-supply-both-shift",
                "excess-demand-supply",
                "consumer-producer-surplus",
                "allocative-efficiency",
                "ped-types",
                "ped-linear-demand",
                "ped-total-revenue",
                "pes-types",
                "commodity-price-volatility",
                "indirect-tax-specific",
                "indirect-tax-ad-valorem",
                "tax-incidence-elasticity",
                "subsidy",
                "price-ceiling",
                "price-floor",
                "minimum-wage",
                "linear-demand-supply",
                "negative-production-externality",
                "negative-consumption-externality",
                "positive-production-externality",
                "positive-consumption-externality",
                "tradable-permits",
                "product-curves",
                "short-run-costs",
                "long-run-average-cost",
                "revenue-perfect-competition",
                "revenue-imperfect-competition",
                "profit-maximisation",
                "perfect-competition-short-run",
                "perfect-competition-long-run",
                "shut-down-break-even",
                "monopoly-profit",
                "monopoly-welfare-loss",
                "monopoly-revenue-maximisation",
                "natural-monopoly",
                "monopolistic-competition-short-run",
                "monopolistic-competition-long-run",
                "kinked-demand",
                "price-discrimination",
                "business-cycle",
                "ad-curve",
                "sras-shift",
                "ad-as-long-run-equilibrium",
                "keynesian-as",
                "keynesian-ad-increase",
                "recessionary-gap-nc",
                "inflationary-gap-nc",
                "recessionary-gap-keynesian",
                "inflationary-gap-keynesian",
                "demand-pull-inflation",
                "cost-push-inflation",
                "deflation",
                "natural-rate-unemployment",
                "demand-deficient-unemployment",
                "real-wage-unemployment",
                "structural-unemployment",
                "short-run-phillips-curve",
                "long-run-phillips-curve",
                "money-market",
                "ad-policy-shift",
                "keynesian-multiplier",
                "crowding-out",
                "economic-growth-adas",
                "supply-side-policy",
                "lorenz-curve",
                "free-trade-importing",
                "free-trade-exporting",
                "comparative-advantage",
                "tariff",
                "import-quota",
                "production-subsidy-trade",
                "export-subsidy",
                "floating-exchange-rate",
                "fixed-exchange-rate",
                "managed-exchange-rate",
                "j-curve",
                "blank-axes"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "SVG image",
            "content": {
              "image/svg+xml": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "description": "Invalid spec",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown template",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "GraphSpec": {
        "type": "object",
        "required": [
          "template"
        ],
        "properties": {
          "template": {
            "type": "string",
            "enum": [
              "ppc-basic",
              "ppc-shift",
              "ppc-opportunity-cost",
              "ppc-actual-potential-growth",
              "demand-curve",
              "demand-shift",
              "supply-curve",
              "supply-shift",
              "market-equilibrium",
              "demand-change-equilibrium",
              "supply-change-equilibrium",
              "demand-supply-both-shift",
              "excess-demand-supply",
              "consumer-producer-surplus",
              "allocative-efficiency",
              "ped-types",
              "ped-linear-demand",
              "ped-total-revenue",
              "pes-types",
              "commodity-price-volatility",
              "indirect-tax-specific",
              "indirect-tax-ad-valorem",
              "tax-incidence-elasticity",
              "subsidy",
              "price-ceiling",
              "price-floor",
              "minimum-wage",
              "linear-demand-supply",
              "negative-production-externality",
              "negative-consumption-externality",
              "positive-production-externality",
              "positive-consumption-externality",
              "tradable-permits",
              "product-curves",
              "short-run-costs",
              "long-run-average-cost",
              "revenue-perfect-competition",
              "revenue-imperfect-competition",
              "profit-maximisation",
              "perfect-competition-short-run",
              "perfect-competition-long-run",
              "shut-down-break-even",
              "monopoly-profit",
              "monopoly-welfare-loss",
              "monopoly-revenue-maximisation",
              "natural-monopoly",
              "monopolistic-competition-short-run",
              "monopolistic-competition-long-run",
              "kinked-demand",
              "price-discrimination",
              "business-cycle",
              "ad-curve",
              "sras-shift",
              "ad-as-long-run-equilibrium",
              "keynesian-as",
              "keynesian-ad-increase",
              "recessionary-gap-nc",
              "inflationary-gap-nc",
              "recessionary-gap-keynesian",
              "inflationary-gap-keynesian",
              "demand-pull-inflation",
              "cost-push-inflation",
              "deflation",
              "natural-rate-unemployment",
              "demand-deficient-unemployment",
              "real-wage-unemployment",
              "structural-unemployment",
              "short-run-phillips-curve",
              "long-run-phillips-curve",
              "money-market",
              "ad-policy-shift",
              "keynesian-multiplier",
              "crowding-out",
              "economic-growth-adas",
              "supply-side-policy",
              "lorenz-curve",
              "free-trade-importing",
              "free-trade-exporting",
              "comparative-advantage",
              "tariff",
              "import-quota",
              "production-subsidy-trade",
              "export-subsidy",
              "floating-exchange-rate",
              "fixed-exchange-rate",
              "managed-exchange-rate",
              "j-curve",
              "blank-axes"
            ],
            "description": "Template id"
          },
          "params": {
            "type": "object",
            "additionalProperties": true,
            "description": "Template options; see GET /api/v1/templates/{id}"
          },
          "title": {
            "type": "string",
            "maxLength": 200
          },
          "labels": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Label text overrides by key"
          },
          "hidden": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "colors": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "dashed": {
            "type": "object",
            "additionalProperties": {
              "type": "boolean"
            }
          },
          "offsets": {
            "type": "object",
            "additionalProperties": {
              "type": "array",
              "items": {
                "type": "number"
              },
              "minItems": 2,
              "maxItems": 2
            },
            "description": "Label nudges in pixels [dx, dy]"
          },
          "annotations": {
            "type": "array",
            "maxItems": 100,
            "items": {
              "type": "object",
              "required": [
                "kind"
              ],
              "properties": {
                "kind": {
                  "type": "string",
                  "enum": [
                    "line",
                    "curve",
                    "point",
                    "area",
                    "shading",
                    "arrow",
                    "text"
                  ]
                },
                "points": {
                  "type": "array",
                  "items": {
                    "type": "array",
                    "items": {
                      "type": "number"
                    },
                    "minItems": 2,
                    "maxItems": 2
                  }
                },
                "at": {
                  "type": "array",
                  "items": {
                    "type": "number"
                  },
                  "minItems": 2,
                  "maxItems": 2
                },
                "from": {
                  "type": "array",
                  "items": {
                    "type": "number"
                  },
                  "minItems": 2,
                  "maxItems": 2
                },
                "to": {
                  "type": "array",
                  "items": {
                    "type": "number"
                  },
                  "minItems": 2,
                  "maxItems": 2
                },
                "label": {
                  "type": "string"
                },
                "text": {
                  "type": "string"
                },
                "xLabel": {
                  "type": "string"
                },
                "yLabel": {
                  "type": "string"
                },
                "role": {
                  "type": "string"
                },
                "dashed": {
                  "type": "boolean"
                },
                "color": {
                  "type": "string"
                },
                "panel": {
                  "type": "string"
                }
              }
            }
          },
          "style": {
            "type": "object",
            "properties": {
              "palette": {
                "enum": [
                  "color",
                  "classic",
                  "mono"
                ]
              },
              "font": {
                "enum": [
                  "sans",
                  "serif",
                  "hand"
                ]
              },
              "fontScale": {
                "type": "number",
                "minimum": 0.6,
                "maximum": 2
              },
              "strokeScale": {
                "type": "number",
                "minimum": 0.5,
                "maximum": 3
              },
              "areaLabels": {
                "enum": [
                  "inside",
                  "legend",
                  "both",
                  "none"
                ]
              },
              "axisLabels": {
                "enum": [
                  "end",
                  "center"
                ]
              },
              "areaOpacity": {
                "type": "number",
                "minimum": 0.05,
                "maximum": 1
              },
              "background": {
                "enum": [
                  "white",
                  "transparent"
                ]
              },
              "showTitle": {
                "type": "boolean"
              },
              "axisArrows": {
                "type": "boolean"
              },
              "showOrigin": {
                "type": "boolean"
              },
              "dots": {
                "type": "boolean"
              }
            }
          },
          "format": {
            "enum": [
              "json",
              "svg"
            ]
          },
          "embedFonts": {
            "type": "boolean"
          }
        }
      },
      "RenderResult": {
        "type": "object",
        "properties": {
          "template": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              }
            }
          },
          "svg": {
            "type": "string"
          },
          "width": {
            "type": "number"
          },
          "height": {
            "type": "number"
          },
          "editUrl": {
            "type": "string",
            "format": "uri"
          },
          "svgUrl": {
            "type": "string",
            "format": "uri"
          },
          "labels": {
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "stats": {
            "type": [
              "array",
              "null"
            ]
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              }
            }
          }
        }
      }
    }
  }
}