{
  "openapi": "3.1.1",
  "info": {
    "title": "Opsdeck ER Diagram from SQL DDL (SVG, Mermaid) API",
    "version": "1.0.0",
    "summary": "Turn CREATE TABLE statements into an ER diagram with crow’s foot notation (SVG, Mermaid).",
    "description": "Every endpoint performs the same calculation as the human page and returns inputs, result, units, assumptions, sources, warnings and computedAt. MCP endpoint: /mcp."
  },
  "servers": [
    {
      "url": "https://opsdeckerdiagram.usefulweb.site"
    }
  ],
  "paths": {
    "/api/v1/calculate": {
      "post": {
        "operationId": "calculate",
        "summary": "Turn CREATE TABLE statements into an ER diagram with crow’s foot notation (SVG, Mermaid).",
        "description": "Paste PostgreSQL, MySQL or SQLite DDL and get an entity-relationship diagram as SVG: tables with their columns and types, primary, foreign and unique keys, and relationships drawn in crow’s foot notation (one, zero-or-one, zero-or-many). Layered layout along foreign keys or a force-directed layout with a fixed seed, plus a Mermaid erDiagram and a text list of relationships. It is the diagram of the Opsdeck Database desk.\n\nMethodology:\n- The DDL is read into the same schema model as the Opsdeck schema diff (columns, keys, foreign keys, checks, indexes; ALTER TABLE applied in order).\n- Layered layout: tables get a rank by the longest chain of foreign keys that point to them (referenced tables left, referencing tables right; cycles are broken at the edge that closes them); four barycentre sweeps order the tables inside each column to reduce crossings.\n- Force-directed layout: Fruchterman–Reingold with a gravity term and a seeded random start (mulberry32), followed by overlap removal; the same seed gives the same diagram everywhere.\n- Crow’s foot notation: the referencing side is zero-or-many (zero-or-one when the foreign-key columns are also unique); the referenced side is exactly one when the foreign-key columns are NOT NULL, otherwise zero-or-one.",
        "tags": [
          "apps"
        ],
        "x-mcp-tool": "opsdeck_er_diagram",
        "x-owner-app": "opsdeck",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OpsdeckErDiagramInput"
              },
              "examples": {
                "example1": {
                  "summary": "Shop schema (PostgreSQL)",
                  "value": {
                    "ddl": "-- Shop schema, version 2\nCREATE TABLE customers (\n  id integer PRIMARY KEY,\n  email varchar(120) NOT NULL UNIQUE,\n  full_name text,\n  phone varchar(40),\n  created_at timestamp DEFAULT CURRENT_TIMESTAMP,\n  country char(2) NOT NULL DEFAULT 'DE'\n);\n\nCREATE TABLE products (\n  sku varchar(32) PRIMARY KEY,\n  title text NOT NULL,\n  price numeric(10,2) NOT NULL CHECK (price >= 0)\n);\n\nCREATE TABLE orders (\n  id integer PRIMARY KEY,\n  customer_id integer NOT NULL REFERENCES customers (id) ON DELETE CASCADE,\n  total numeric(10,2) NOT NULL DEFAULT 0,\n  status varchar(20) NOT NULL DEFAULT 'new',\n  shipped_at timestamp\n);\nCREATE INDEX orders_status_idx ON orders (status, shipped_at);\n\nCREATE TABLE order_items (\n  order_id integer NOT NULL REFERENCES orders (id),\n  sku varchar(32) NOT NULL REFERENCES products (sku),\n  qty integer NOT NULL CHECK (qty > 0),\n  PRIMARY KEY (order_id, sku)\n);\n",
                    "dialect": "postgresql",
                    "layout": "layered",
                    "title": "Shop schema"
                  }
                },
                "example2": {
                  "summary": "Blog with tags and threaded comments (MySQL, force layout)",
                  "value": {
                    "ddl": "CREATE TABLE `authors` (\n  `id` int unsigned NOT NULL AUTO_INCREMENT,\n  `handle` varchar(40) NOT NULL,\n  `bio` text,\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `uk_handle` (`handle`)\n) ENGINE=InnoDB;\n\nCREATE TABLE `posts` (\n  `id` int unsigned NOT NULL AUTO_INCREMENT,\n  `author_id` int unsigned NOT NULL,\n  `title` varchar(200) NOT NULL,\n  `published_at` datetime DEFAULT NULL,\n  PRIMARY KEY (`id`),\n  KEY `idx_published` (`published_at`),\n  CONSTRAINT `fk_posts_author` FOREIGN KEY (`author_id`) REFERENCES `authors` (`id`)\n) ENGINE=InnoDB;\n\nCREATE TABLE `tags` (\n  `id` int unsigned NOT NULL AUTO_INCREMENT,\n  `name` varchar(40) NOT NULL,\n  PRIMARY KEY (`id`)\n);\n\nCREATE TABLE `post_tags` (\n  `post_id` int unsigned NOT NULL,\n  `tag_id` int unsigned NOT NULL,\n  PRIMARY KEY (`post_id`, `tag_id`),\n  CONSTRAINT `fk_pt_post` FOREIGN KEY (`post_id`) REFERENCES `posts` (`id`) ON DELETE CASCADE,\n  CONSTRAINT `fk_pt_tag` FOREIGN KEY (`tag_id`) REFERENCES `tags` (`id`)\n);\n\nCREATE TABLE `comments` (\n  `id` int unsigned NOT NULL AUTO_INCREMENT,\n  `post_id` int unsigned NOT NULL,\n  `parent_id` int unsigned DEFAULT NULL,\n  `body` text NOT NULL,\n  PRIMARY KEY (`id`),\n  CONSTRAINT `fk_c_post` FOREIGN KEY (`post_id`) REFERENCES `posts` (`id`),\n  CONSTRAINT `fk_c_parent` FOREIGN KEY (`parent_id`) REFERENCES `comments` (`id`)\n);\n",
                    "dialect": "mysql",
                    "layout": "force",
                    "seed": 7,
                    "title": "Blog schema"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OpsdeckErDiagramResult"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid input (see errors[]).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (see Retry-After).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tool": {
      "get": {
        "operationId": "getToolDefinition",
        "summary": "Inputs, output schema and examples for this tool",
        "responses": {
          "200": {
            "description": "Tool definition.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Problem": {
        "type": "object",
        "description": "RFC 9457 problem details. 422 responses include per-field `errors`.",
        "required": [
          "type",
          "title",
          "status",
          "detail"
        ],
        "properties": {
          "type": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "status": {
            "type": "integer"
          },
          "detail": {
            "type": "string"
          },
          "code": {
            "type": "string"
          },
          "errors": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "field",
                "message"
              ],
              "properties": {
                "field": {
                  "type": "string"
                },
                "message": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "OpsdeckErDiagramInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "ddl": {
            "title": "Schema (DDL)",
            "description": "CREATE TABLE (and CREATE INDEX / ALTER TABLE) statements.",
            "type": "string",
            "maxLength": 500000,
            "default": "-- Shop schema, version 2\nCREATE TABLE customers (\n  id integer PRIMARY KEY,\n  email varchar(120) NOT NULL UNIQUE,\n  full_name text,\n  phone varchar(40),\n  created_at timestamp DEFAULT CURRENT_TIMESTAMP,\n  country char(2) NOT NULL DEFAULT 'DE'\n);\n\nCREATE TABLE products (\n  sku varchar(32) PRIMARY KEY,\n  title text NOT NULL,\n  price numeric(10,2) NOT NULL CHECK (price >= 0)\n);\n\nCREATE TABLE orders (\n  id integer PRIMARY KEY,\n  customer_id integer NOT NULL REFERENCES customers (id) ON DELETE CASCADE,\n  total numeric(10,2) NOT NULL DEFAULT 0,\n  status varchar(20) NOT NULL DEFAULT 'new',\n  shipped_at timestamp\n);\nCREATE INDEX orders_status_idx ON orders (status, shipped_at);\n\nCREATE TABLE order_items (\n  order_id integer NOT NULL REFERENCES orders (id),\n  sku varchar(32) NOT NULL REFERENCES products (sku),\n  qty integer NOT NULL CHECK (qty > 0),\n  PRIMARY KEY (order_id, sku)\n);\n"
          },
          "dialect": {
            "title": "SQL dialect",
            "type": "string",
            "enum": [
              "postgresql",
              "mysql",
              "sqlite"
            ],
            "default": "postgresql"
          },
          "layout": {
            "title": "Layout",
            "type": "string",
            "enum": [
              "layered",
              "force"
            ],
            "default": "layered"
          },
          "seed": {
            "title": "Layout seed (force-directed)",
            "description": "The same seed always gives the same picture; try another seed for another arrangement.",
            "type": "integer",
            "minimum": 0,
            "maximum": 2147483647,
            "default": 1
          },
          "showTypes": {
            "title": "Show column types",
            "type": "boolean",
            "default": true
          },
          "title": {
            "title": "Diagram title",
            "type": "string",
            "maxLength": 120,
            "default": ""
          }
        },
        "required": []
      },
      "OpsdeckErDiagramResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "tool",
          "computedAt",
          "inputs",
          "summary",
          "result",
          "units",
          "assumptions",
          "sources",
          "warnings",
          "presentation",
          "links"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "const": "1.0"
          },
          "tool": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "version"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "version": {
                "type": "string"
              }
            }
          },
          "computedAt": {
            "type": "string",
            "format": "date-time"
          },
          "inputs": {
            "type": "object",
            "description": "Normalised inputs actually used, including applied defaults."
          },
          "summary": {
            "type": "string"
          },
          "result": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "dialect",
              "tables",
              "issues",
              "diagram",
              "ddl"
            ],
            "properties": {
              "dialect": {
                "enum": [
                  "postgresql",
                  "mysql",
                  "sqlite"
                ]
              },
              "tables": {
                "type": "array",
                "items": {
                  "type": "object",
                  "required": [
                    "name",
                    "columns",
                    "primaryKey",
                    "foreignKeys",
                    "uniques",
                    "indexes",
                    "checks"
                  ],
                  "properties": {
                    "name": {
                      "type": "string"
                    },
                    "columns": {
                      "type": "number"
                    },
                    "primaryKey": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "foreignKeys": {
                      "type": "number"
                    },
                    "uniques": {
                      "type": "number"
                    },
                    "indexes": {
                      "type": "number"
                    },
                    "checks": {
                      "type": "number"
                    }
                  }
                }
              },
              "issues": {
                "type": "array",
                "items": {
                  "type": "object"
                },
                "description": "Parser notes: skipped statements, references to undefined tables."
              },
              "diagram": {
                "type": "object",
                "required": [
                  "svg",
                  "width",
                  "height",
                  "boxes",
                  "relationships",
                  "mermaid",
                  "description"
                ],
                "properties": {
                  "svg": {
                    "type": "string"
                  },
                  "width": {
                    "type": "number"
                  },
                  "height": {
                    "type": "number"
                  },
                  "boxes": {
                    "type": "array",
                    "items": {
                      "type": "object"
                    }
                  },
                  "relationships": {
                    "type": "array",
                    "items": {
                      "type": "object"
                    }
                  },
                  "mermaid": {
                    "type": "string"
                  },
                  "description": {
                    "type": "string"
                  }
                }
              },
              "ddl": {
                "type": "string",
                "description": "The schema model written back as normalised DDL."
              }
            }
          },
          "units": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "assumptions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "title"
              ],
              "properties": {
                "title": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "publisher": {
                  "type": "string"
                },
                "retrievedAt": {
                  "type": "string"
                },
                "note": {
                  "type": "string"
                }
              }
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "presentation": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Display blocks used by the human page (same data)."
          },
          "links": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "human",
              "api",
              "openapi",
              "methodology"
            ],
            "properties": {
              "human": {
                "type": "string",
                "format": "uri"
              },
              "api": {
                "type": "string",
                "format": "uri"
              },
              "openapi": {
                "type": "string",
                "format": "uri"
              },
              "methodology": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      }
    }
  }
}