GET
/admin/projects/{projectId}/openapi/export
Export the Project as an OpenAPI document

One components.schemas entry per data type (reverse-mapping schema_json back into OpenAPI properties - the inverse of OpenAPI Import's own mapping, sharing the same format lookup table so the two directions can never silently drift apart), and one paths entry per configured mock route. Reflects the current route/type configuration, not the master template's actual generated data. Contains only what the Runtime API itself exposes - no Spaces, members, API keys, or plan/billing information. An empty Project (no types/routes yet) still returns a valid, empty document.

Example response (abridged)

{
  "openapi": "3.0.0",
  "info": {
    "title": "My Project Mock API",
    "version": "1.0.0"
  },
  "paths": {
    "/users": {
      "get": {
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/User"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/users/{id}/posts": {
      "get": {
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "number"
            },
            "description": "Resolves against User.id (relationship id 1)."
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Post"
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "User": {
        "type": "object",
        "required": [
          "email"
        ],
        "properties": {
          "id": {
            "type": "number"
          },
          "email": {
            "type": "string",
            "format": "email"
          }
        }
      }
    }
  }
}
Reverse schema mapping

Always lossless for anything this service's own Data Types could produce, since export only ever needs to invert its own vocabulary:

  • string/number/boolean/array/object map directly onto the same OpenAPI type names; a date field becomes { type: "string", format: "date-time" }.
  • enum passes through unchanged.
  • min/max on a numeric field become minimum/maximum.
  • A format with a genuine OpenAPI standard equivalent (only email and uuid - the same two OpenAPI Import maps in the other direction) keeps that format keyword. Every other format this service supports (firstName, phoneNumber, paragraph, ...) has no OpenAPI standard equivalent, so it becomes a description noting the generator vocabulary instead of a fabricated format value an external consumer might misinterpret.
  • required is collected per nesting level into the OpenAPI required array, exactly mirroring how validateRecordAgainstSchema enforces it.
Path parameters and response shapes

A route's own record-lookup parameter (the path's trailing {param}) documents which field it resolves against and that field's actual schema type. A nested route's parent parameter additionally names the relationship it depends on (project_relationships informs the parameter's description, per api-specification.md §25 - it doesn't need its own OpenAPI construct beyond that). A GET on a collection path (no record lookup) returns an array response schema; every other method - including a collection-path POST, which creates one record - returns a single-record schema, never an array (a real bug caught by exporting an actual Project and inspecting the response: a first pass wrapped every collection-path method's response in an array, including POST).