{
  "openapi": "3.1.0",
  "info": {
    "title": "FERS Cloud API",
    "version": "1.0.0",
    "description": "Build, validate and hand off FERS finite-element structural models over HTTP. Authenticate with a FersCloud API key (create one at ferscloud.com → Profile → API Keys) sent as the `X-API-Key` header.\n\nFERS Cloud runs no solver compute. `POST /api/sdk/solve` and `POST /api/sdk/check-beam` validate the model and return `status: \"solve_in_browser\"` with an `open_url`: a public link, valid for 24 hours, that solves the model in the browser of whoever opens it and shows the displacements, internal forces, reactions and code (unity) checks there. Give the user that link. This API returns no results; to get them in code, solve where the code runs: the Python SDK (`pip install FERS`, then `fers solve model.json`) or the local MCP server (`npx -y @ferscloud/fers-mcp`).\n\nStart with `GET /api/sdk/schema` and `GET /api/sdk/sections` rather than guessing the model format or a section name, then `POST /api/sdk/validate`. No endpoint is metered and there is no solve quota; the tier only caps model size: Free up to 100 members, Pro up to 10,000.\n\nThe MCP server at https://ferscloud.com/api/mcp is the richer authoring surface (arbitrary geometry, modal, buckling, seismic, plates, EN 1990 combinations) if your client speaks MCP."
  },
  "servers": [
    {
      "url": "https://dev.ferscloud.com"
    }
  ],
  "security": [
    {
      "ApiKeyAuth": []
    }
  ],
  "paths": {
    "/api/sdk/solve": {
      "post": {
        "operationId": "solveModel",
        "summary": "Get a link that solves a FERS model in the browser",
        "description": "Validates the model and, if it passes, parks it as a public draft and returns `open_url`: a link, valid for 24 hours, that solves the model in the browser of whoever opens it (at that account's tier) and shows the displacements, internal forces, reactions and unity checks there. This endpoint runs no solver and returns no results. A model that fails validation gets 422 `invalid_model` with `errors` and `warnings` instead of a link. Not metered.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "model"
                ],
                "properties": {
                  "model": {
                    "type": "object",
                    "description": "A full FERS structural model (schema_version, settings, model, analysis). Build one with the MCP create_beam/create_frame tools or start from a template; may also be sent as a JSON string.",
                    "additionalProperties": true
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Validated and parked. Give the user `open_url`; the solve runs in their browser.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "status",
                    "open_url",
                    "expires_at",
                    "model_ref",
                    "members",
                    "tier",
                    "local_server",
                    "python",
                    "note"
                  ],
                  "properties": {
                    "status": {
                      "type": "string",
                      "enum": [
                        "solve_in_browser"
                      ]
                    },
                    "open_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "Public link that solves the model in a browser and shows the results there. Give it to the user."
                    },
                    "expires_at": {
                      "type": "string",
                      "format": "date-time",
                      "description": "When the link stops working: 24 hours after it was made, or earlier once 50 newer drafts exist."
                    },
                    "model_ref": {
                      "type": "string",
                      "description": "Handle of the parked draft, e.g. draft_<id>."
                    },
                    "members": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "description": "Members as the engine counts them for the tier limit (plates excluded); null if the model could not be parsed."
                    },
                    "tier": {
                      "type": "object",
                      "properties": {
                        "free_member_limit": {
                          "type": "integer"
                        },
                        "pro_member_limit": {
                          "type": "integer"
                        },
                        "solves_at": {
                          "type": "string",
                          "description": "Whose tier applies: the account that opens the link."
                        }
                      }
                    },
                    "local_server": {
                      "type": "object",
                      "description": "How to install the local FERS MCP server, which solves on the user's machine and returns the results to the agent.",
                      "properties": {
                        "npx": {
                          "type": "string"
                        },
                        "claude_code": {
                          "type": "string"
                        },
                        "claude_desktop": {
                          "type": "string"
                        },
                        "docs": {
                          "type": "string"
                        }
                      }
                    },
                    "python": {
                      "type": "string",
                      "description": "How to solve with the Python SDK instead."
                    },
                    "note": {
                      "type": "string"
                    },
                    "validation": {
                      "type": "object",
                      "properties": {
                        "valid": {
                          "type": "boolean"
                        },
                        "warnings": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "path": {
                                "type": "string",
                                "description": "Where the problem is, e.g. \"model.members[3].section\"."
                              },
                              "message": {
                                "type": "string"
                              },
                              "severity": {
                                "type": "string",
                                "enum": [
                                  "error",
                                  "warning"
                                ]
                              }
                            },
                            "required": [
                              "path",
                              "message",
                              "severity"
                            ]
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The body has no `model`, or it could not be read as JSON",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "code": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "code": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "413": {
            "description": "The model JSON is larger than the size limit",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "code": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "422": {
            "description": "The model fails validation (code: invalid_model); no link is made",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "error",
                    "code",
                    "errors",
                    "warnings"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "code": {
                      "type": "string",
                      "enum": [
                        "invalid_model"
                      ]
                    },
                    "errors": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "path": {
                            "type": "string",
                            "description": "Where the problem is, e.g. \"model.members[3].section\"."
                          },
                          "message": {
                            "type": "string"
                          },
                          "severity": {
                            "type": "string",
                            "enum": [
                              "error",
                              "warning"
                            ]
                          }
                        },
                        "required": [
                          "path",
                          "message",
                          "severity"
                        ]
                      }
                    },
                    "warnings": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "path": {
                            "type": "string",
                            "description": "Where the problem is, e.g. \"model.members[3].section\"."
                          },
                          "message": {
                            "type": "string"
                          },
                          "severity": {
                            "type": "string",
                            "enum": [
                              "error",
                              "warning"
                            ]
                          }
                        },
                        "required": [
                          "path",
                          "message",
                          "severity"
                        ]
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/sdk/validate": {
      "post": {
        "operationId": "validateModel",
        "summary": "Validate a model against the solver schema (free)",
        "description": "Checks the model against the solver's own schema without solving it: field-level errors (missing/wrong-typed fields, invalid enum values), broken id references (a member pointing at a missing node, a load on a deleted member, duplicate ids), and warnings for unknown fields under analysis.* — which the engine silently ignores, so a typo becomes a wrong answer rather than an error. Does not count as a solve.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "model"
                ],
                "properties": {
                  "model": {
                    "type": "object",
                    "description": "A full FERS structural model (schema_version, settings, model, analysis). Build one with the MCP create_beam/create_frame tools or start from a template; may also be sent as a JSON string.",
                    "additionalProperties": true
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Validation result",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "valid": {
                      "type": "boolean"
                    },
                    "errors": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "path": {
                            "type": "string",
                            "description": "Where the problem is, e.g. \"model.members[3].section\"."
                          },
                          "message": {
                            "type": "string"
                          },
                          "severity": {
                            "type": "string",
                            "enum": [
                              "error",
                              "warning"
                            ]
                          }
                        },
                        "required": [
                          "path",
                          "message",
                          "severity"
                        ]
                      }
                    },
                    "warnings": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "path": {
                            "type": "string",
                            "description": "Where the problem is, e.g. \"model.members[3].section\"."
                          },
                          "message": {
                            "type": "string"
                          },
                          "severity": {
                            "type": "string",
                            "enum": [
                              "error",
                              "warning"
                            ]
                          }
                        },
                        "required": [
                          "path",
                          "message",
                          "severity"
                        ]
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "code": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/api/sdk/create-beam": {
      "post": {
        "operationId": "createBeam",
        "summary": "Generate a solver-ready beam model (named or rectangular section)",
        "description": "Build a single-span beam model JSON from simple inputs — a named library section (IPE300, HEA200, UB 457x191x67, …) or a rectangle, plus span, support and load; pass the returned `model` straight to solveModel. Units: metres, mm, kN.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "span_m"
                ],
                "properties": {
                  "span_m": {
                    "type": "number",
                    "description": "Beam span in metres. (The server also forgives the alias names span/length, case-insensitively.)"
                  },
                  "support": {
                    "type": "string",
                    "enum": [
                      "simply_supported",
                      "cantilever"
                    ],
                    "description": "Support type (default simply_supported)"
                  },
                  "section": {
                    "type": "string",
                    "description": "Library section name, e.g. IPE300, HEA200, 'RHS 100x50x5', 'UB 457x191x67'. Overrides height_mm/width_mm."
                  },
                  "height_mm": {
                    "type": "number",
                    "description": "Rectangular section depth in mm (default 200)"
                  },
                  "width_mm": {
                    "type": "number",
                    "description": "Rectangular section width in mm (default 100)"
                  },
                  "material": {
                    "type": "string",
                    "enum": [
                      "steel_S235",
                      "steel_S355",
                      "aluminium_6061",
                      "concrete_C30"
                    ],
                    "description": "Material preset (default steel_S235; shorthand S235/S275/S355/S460 is recognized)"
                  },
                  "load_type": {
                    "type": "string",
                    "enum": [
                      "udl",
                      "point"
                    ],
                    "description": "Load type (default udl)"
                  },
                  "load_kn": {
                    "type": "number",
                    "description": "UDL in kN/m or point load in kN, downward (default 10). (The server also forgives the alias names load/udl/uniform_load, and point_load — which implies load_type 'point'.)"
                  },
                  "load_position_frac": {
                    "type": "number",
                    "description": "Point-load position along the span, 0..1 (default 0.5)"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The generated model (pass `model` to solveModel)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "model": {
                      "type": "object",
                      "additionalProperties": true
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid parameters (missing span, conflicting alias values, unknown section/material/support/load_type)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "code": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "code": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/api/sdk/check-beam": {
      "post": {
        "operationId": "checkBeam",
        "summary": "Check a steel beam to EN 1993-1-1 (Eurocode 3)",
        "description": "Builds a single-span beam with a ULS load combination and an EC3 member check (bending, shear, combined N+M, lateral-torsional buckling), parks it as a public draft and returns `open_url`: a link, valid for 24 hours, that evaluates the check in the browser of whoever opens it and shows the governing utilization and the per-rule breakdown there. Also returns the built `model`, so a caller with the FERS engine can solve it directly. This endpoint runs no solver. Common parameter aliases are accepted case-insensitively (span/length, load_kN_per_m/uniform_load, laterally_restrained). A named library section (European, UK or US) is required. Units: metres, kN, kN/m. Not metered.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "span_m",
                  "section"
                ],
                "properties": {
                  "span_m": {
                    "type": "number",
                    "description": "Beam span in metres. (The server also forgives the alias names span/length, case-insensitively.)"
                  },
                  "section": {
                    "type": "string",
                    "description": "Library section name, e.g. IPE300, HEA200, 'RHS 100x50x5', 'UB 457x191x67'"
                  },
                  "support": {
                    "type": "string",
                    "enum": [
                      "simply_supported",
                      "cantilever",
                      "fixed"
                    ],
                    "description": "Support type (default simply_supported)"
                  },
                  "material": {
                    "type": "string",
                    "enum": [
                      "steel_S235",
                      "steel_S275",
                      "steel_S355",
                      "steel_S460"
                    ],
                    "description": "Steel grade (default steel_S235; shorthand S235/S275/S355/S460 is recognized)"
                  },
                  "udl": {
                    "type": "number",
                    "description": "Uniformly distributed load in kN/m, downward. (The server also forgives the alias names load_kN_per_m/uniform_load.)"
                  },
                  "point_load": {
                    "type": "number",
                    "description": "Point load in kN, downward"
                  },
                  "point_position": {
                    "type": "number",
                    "description": "Point-load position along the span, 0..1 (default mid-span)"
                  },
                  "uls_factor": {
                    "type": "number",
                    "description": "ULS load factor applied to the load (default 1.35)"
                  },
                  "restrained": {
                    "type": "boolean",
                    "description": "Set true if the compression flange is continuously laterally restrained (e.g. by decking or a slab) — the LTB check is then skipped. Alias: laterally_restrained. Default false."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Built and parked. Give the user `open_url`; the check is evaluated in their browser.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "status",
                    "open_url",
                    "expires_at",
                    "model_ref",
                    "members",
                    "tier",
                    "local_server",
                    "python",
                    "note",
                    "model"
                  ],
                  "properties": {
                    "status": {
                      "type": "string",
                      "enum": [
                        "solve_in_browser"
                      ]
                    },
                    "open_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "Public link that solves the model in a browser and shows the results there. Give it to the user."
                    },
                    "expires_at": {
                      "type": "string",
                      "format": "date-time",
                      "description": "When the link stops working: 24 hours after it was made, or earlier once 50 newer drafts exist."
                    },
                    "model_ref": {
                      "type": "string",
                      "description": "Handle of the parked draft, e.g. draft_<id>."
                    },
                    "members": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "description": "Members as the engine counts them for the tier limit (plates excluded); null if the model could not be parsed."
                    },
                    "tier": {
                      "type": "object",
                      "properties": {
                        "free_member_limit": {
                          "type": "integer"
                        },
                        "pro_member_limit": {
                          "type": "integer"
                        },
                        "solves_at": {
                          "type": "string",
                          "description": "Whose tier applies: the account that opens the link."
                        }
                      }
                    },
                    "local_server": {
                      "type": "object",
                      "description": "How to install the local FERS MCP server, which solves on the user's machine and returns the results to the agent.",
                      "properties": {
                        "npx": {
                          "type": "string"
                        },
                        "claude_code": {
                          "type": "string"
                        },
                        "claude_desktop": {
                          "type": "string"
                        },
                        "docs": {
                          "type": "string"
                        }
                      }
                    },
                    "python": {
                      "type": "string",
                      "description": "How to solve with the Python SDK instead."
                    },
                    "note": {
                      "type": "string"
                    },
                    "model": {
                      "type": "object",
                      "additionalProperties": true,
                      "description": "The built FERS model (beam, ULS combination and EC3 check), ready to solve with the Python SDK or the local MCP server."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid parameters (missing span/section, conflicting alias values, unknown section/material/support)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "code": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "code": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/api/sdk/schema": {
      "get": {
        "operationId": "getSchema",
        "summary": "Look up the FERS model schema (free)",
        "description": "The authoritative JSON Schema the solver itself is generated from. Call with no `root` for an index of the useful roots (geometry, loads, analysis, checks); pass a `root` for that schema plus everything it references. Use this instead of guessing field names: the engine silently ignores unknown keys under analysis.* and returns a default rather than an error. Free, unmetered.",
        "parameters": [
          {
            "name": "root",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Schema to expand, e.g. BucklingAnalysisSettings, MemberHinge, LoadCase, Model. Omit for the index."
          }
        ],
        "responses": {
          "200": {
            "description": "Schema index, or the requested schema closure",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "code": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Unknown schema root",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "code": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/api/sdk/sections": {
      "get": {
        "operationId": "listSections",
        "summary": "List or look up named steel sections (free)",
        "description": "The named steel sections FERS knows: European (IPE, HEA, HEB, HEM, UPE, RHS, SHS, CHS, L) and UK (UB, UC, PFC). solveModel's beam builders and checkBeam require one of these names. Pass `family` to narrow the list, or `name` for one section's full properties in SI units — including the wel/wpl moduli and warping constant an EN 1993-1-1 check needs. Free, unmetered.",
        "parameters": [
          {
            "name": "family",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Filter by family/prefix, e.g. IPE, HEA, RHS."
          },
          {
            "name": "name",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Return one section's full properties, e.g. IPE300."
          }
        ],
        "responses": {
          "200": {
            "description": "Section names, or one section's properties",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "code": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Unknown section or family",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "code": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/api/sdk/me": {
      "get": {
        "operationId": "getMe",
        "summary": "Get the authenticated account (id, email, premium status)",
        "responses": {
          "200": {
            "description": "Account info",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "user_id": {
                      "type": "string"
                    },
                    "email": {
                      "type": "string"
                    },
                    "is_premium": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "code": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/api/sdk/models": {
      "get": {
        "operationId": "listModels",
        "summary": "List saved models",
        "responses": {
          "200": {
            "description": "Model list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "models": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          },
                          "description": {
                            "type": "string"
                          },
                          "updatedAt": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "saveModel",
        "summary": "Save a model to the account (results are never stored)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name",
                  "model"
                ],
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "description": {
                    "type": "string"
                  },
                  "model": {
                    "type": "object",
                    "description": "A full FERS structural model (schema_version, settings, model, analysis). Build one with the MCP create_beam/create_frame tools or start from a template; may also be sent as a JSON string.",
                    "additionalProperties": true
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Saved",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          }
        }
      }
    },
    "/api/sdk/models/{id}": {
      "get": {
        "operationId": "getModel",
        "summary": "Download a saved model's full JSON by id",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The model JSON",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "code": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "ApiKeyAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "X-API-Key",
        "description": "A FersCloud API key in the form `keyId.secret` (create at ferscloud.com → Profile → API Keys). A `Authorization: Bearer <sdk-jwt>` token is also accepted."
      }
    }
  }
}