{
  "openapi": "3.1.0",
  "info": {
    "title": "Mockfly Public API",
    "version": "1.0.0",
    "summary": "Manage Mockfly mock projects, endpoints and responses programmatically.",
    "description": "REST API to manage your [Mockfly](https://mockfly.dev) mock API projects programmatically: create a project from a script, import a whole project from your CI pipeline, or keep your mocks in sync with your codebase without opening the dashboard.\n\nAuthentication uses two different API keys sent as the **raw** value of the `Authorization` header (there is no `Bearer` prefix):\n\n- **Account API key** for the `/public/projects` operations.\n- **Project API key** for the `/public/endpoints` operations (endpoints and their responses).\n\nThe API Hub catalog (`GET /hub/catalog`) is completely open and needs no key.\n\n---\n\n**Maintenance note:** this specification is maintained by hand in the [`mockfly-page`](https://github.com/zamarrowski/mockfly-page) repository (`public/openapi.json`); it is not generated from the backend. The functional source of truth is the [Public API documentation](https://mockfly.dev/docs/public-api/), so if the two ever disagree, trust the docs and please [open an issue](https://github.com/zamarrowski/mockfly-page/issues).",
    "contact": {
      "name": "Mockfly",
      "url": "https://mockfly.dev/docs/public-api/"
    },
    "license": {
      "name": "Proprietary",
      "url": "https://mockfly.dev/privacy-policy/"
    }
  },
  "externalDocs": {
    "description": "Public API documentation",
    "url": "https://mockfly.dev/docs/public-api/"
  },
  "servers": [
    {
      "url": "https://api.mockfly.dev",
      "description": "Production"
    }
  ],
  "tags": [
    {
      "name": "Projects",
      "description": "Account-level operations on projects. Authenticated with the account API key.",
      "externalDocs": {
        "url": "https://mockfly.dev/docs/public-api/#manageProjects"
      }
    },
    {
      "name": "Endpoints",
      "description": "Operations on the endpoints of a single project. Authenticated with that project's API key.",
      "externalDocs": {
        "url": "https://mockfly.dev/docs/public-api/#manageEndpoints"
      }
    },
    {
      "name": "Responses",
      "description": "Operations on the mock responses of an endpoint, including their conditional rules. Authenticated with the project API key.",
      "externalDocs": {
        "url": "https://mockfly.dev/docs/conditional-response-mock-api/"
      }
    },
    {
      "name": "API Hub",
      "description": "Free public mock APIs. No authentication required.",
      "externalDocs": {
        "url": "https://mockfly.dev/api-hub/"
      }
    }
  ],
  "security": [
    {
      "accountApiKey": []
    }
  ],
  "paths": {
    "/public/projects": {
      "get": {
        "operationId": "listProjects",
        "tags": ["Projects"],
        "summary": "List your projects",
        "description": "Returns every project the authenticated account can access, either as its admin or as an invited member. The listing is intentionally compact: each item only carries the project id, its name and its slug. Use the project id with `updateProject` or `deleteProject`, and the slug to build the mock server URL. The project API key is not part of this response; read it from the project configuration in the dashboard.",
        "security": [{ "accountApiKey": [] }],
        "responses": {
          "200": {
            "description": "The projects the account has access to.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["results"],
                  "properties": {
                    "results": {
                      "type": "array",
                      "description": "One entry per accessible project.",
                      "items": { "$ref": "#/components/schemas/ProjectSummary" }
                    }
                  }
                },
                "example": {
                  "results": [
                    {
                      "_id": "665f1c2e8b3a4c0012ab34cd",
                      "name": "My project",
                      "slug": "0f9a7c1e-4b3d-4f19-9a2e-6c8d5b1f7a30"
                    }
                  ]
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      },
      "post": {
        "operationId": "createProject",
        "tags": ["Projects"],
        "summary": "Create an empty project",
        "description": "Creates a new project owned (administered) by the authenticated account. Only `name` is required; `tags` are optional labels used to organise projects in the dashboard.\n\nThe response is the full project, including the two values you need afterwards: its `slug` (which forms the mock server URL, `https://<slug>.mockfly.dev`) and its `privateApiKey` (the project API key required by every endpoint and response operation).\n\nFree accounts can administer **1 project**; creating a second one fails with `429`. Upgrading to premium removes the limit.",
        "security": [{ "accountApiKey": [] }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/CreateProjectRequest" },
              "example": {
                "name": "My project",
                "tags": [{ "name": "backend", "color": "#48cfad" }]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The project was created.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Project" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "429": { "$ref": "#/components/responses/PlanLimitExceeded" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/public/projects/import": {
      "post": {
        "operationId": "importProject",
        "tags": ["Projects"],
        "summary": "Create a project with its endpoints and responses in one call",
        "description": "Creates a project together with all of its endpoints and their mock responses in a single request. The payload is the same JSON the dashboard produces with \"Export to JSON\", so you can export an existing project and re-import it programmatically — which is what makes this the operation to use when seeding mocks from CI.\n\nRules worth knowing before calling it:\n\n- Only `project.name` is required. `endpoints` may be omitted or empty.\n- Every endpoint must carry `path`, `method` and a `responses` array (an empty array is allowed).\n- When the endpoint serves JSON (the default), `bodyExample` and each response `body` must be JSON objects or arrays — **not** strings containing JSON.\n- The import is all-or-nothing: if any endpoint fails validation nothing is created, and the half-built project is rolled back.\n\nThe same free plan limits apply to the resulting project (1 administered project, 4 endpoints per project, 2 responses per endpoint), so an import that would exceed them fails with `429`.",
        "security": [{ "accountApiKey": [] }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/ImportProjectRequest" },
              "example": {
                "project": { "name": "Imported project" },
                "endpoints": [
                  {
                    "path": "/users",
                    "method": "GET",
                    "description": "List users",
                    "delay": 0,
                    "headers": [],
                    "bodyExample": {},
                    "showInDoc": true,
                    "returnRandomResponse": false,
                    "proxyConfiguration": "default",
                    "position": 0,
                    "responses": [
                      {
                        "name": "Success",
                        "status": 200,
                        "body": { "users": [] },
                        "isEnabled": true,
                        "rules": []
                      }
                    ]
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The project and everything inside it was created.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Project" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "429": { "$ref": "#/components/responses/PlanLimitExceeded" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/public/projects/{projectId}": {
      "parameters": [{ "$ref": "#/components/parameters/ProjectId" }],
      "patch": {
        "operationId": "updateProject",
        "tags": ["Projects"],
        "summary": "Update a project",
        "description": "Renames a project and/or replaces its tags. Both fields are optional, but sending neither is a no-op. `tags` is replaced wholesale rather than merged, so send the complete list you want to keep.\n\nOnly the project **admin** can update a project: a member who was merely invited to it gets `403`.",
        "security": [{ "accountApiKey": [] }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/UpdateProjectRequest" },
              "example": {
                "name": "Renamed project",
                "tags": [{ "name": "updated", "color": "#48cfad" }]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The project after the update.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Project" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      },
      "delete": {
        "operationId": "deleteProject",
        "tags": ["Projects"],
        "summary": "Delete a project",
        "description": "Deletes a project and stops its mock server from answering. The deletion is a soft delete on Mockfly's side, but it is not reversible through this API — export the project first if you may need it back.\n\nOnly the project **admin** can delete a project: a member who was merely invited to it gets `403`.",
        "security": [{ "accountApiKey": [] }],
        "responses": {
          "200": {
            "description": "The project was deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "deleted": {
                      "type": "string",
                      "description": "Always `ok` when the deletion succeeded.",
                      "const": "ok"
                    }
                  }
                },
                "example": { "deleted": "ok" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/public/endpoints": {
      "get": {
        "operationId": "listEndpoints",
        "tags": ["Endpoints"],
        "summary": "List the endpoints of the project",
        "description": "Returns every endpoint of the project the API key belongs to — there is no project parameter, the key itself selects the project.\n\nThis listing is trimmed for size: `description`, `bodyExample` and `defaultResponse` are omitted from each endpoint. Call `getEndpoint` when you need the full endpoint with its populated responses.",
        "security": [{ "projectApiKey": [] }],
        "responses": {
          "200": {
            "description": "The endpoints of the project.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["results"],
                  "properties": {
                    "results": {
                      "type": "array",
                      "description": "One entry per endpoint, without `description`, `bodyExample` or `defaultResponse`.",
                      "items": { "$ref": "#/components/schemas/Endpoint" }
                    }
                  }
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      },
      "post": {
        "operationId": "createEndpoint",
        "tags": ["Endpoints"],
        "summary": "Create an endpoint",
        "description": "Adds an endpoint to the project. `path`, `method` and `projectId` are all required, and `projectId` must be the id of the very project the API key belongs to.\n\nA project cannot hold two endpoints with the same `path` and `method`; attempting it fails. Paths may contain URL parameters written with a colon, as in `/users/:id`.\n\nA new endpoint starts with no responses — call `createResponse` next. Free plans allow **4 endpoints per project**, and the fifth one fails with `429`.",
        "security": [{ "projectApiKey": [] }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/CreateEndpointRequest" },
              "example": {
                "projectId": "665f1c2e8b3a4c0012ab34cd",
                "path": "/users",
                "method": "GET"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The endpoint was created.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Endpoint" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "429": { "$ref": "#/components/responses/PlanLimitExceeded" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/public/endpoints/{endpointId}": {
      "parameters": [{ "$ref": "#/components/parameters/EndpointId" }],
      "get": {
        "operationId": "getEndpoint",
        "tags": ["Endpoints"],
        "summary": "Get an endpoint",
        "description": "Returns a single endpoint with its mock responses fully expanded, including each response's `rules` and `bodyHistory`. This is the operation to call before editing a response, since it is where the response ids come from.\n\nThe endpoint must belong to the project the API key belongs to.",
        "security": [{ "projectApiKey": [] }],
        "responses": {
          "200": {
            "description": "The endpoint with its responses expanded.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/EndpointDetail" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      },
      "patch": {
        "operationId": "updateEndpoint",
        "tags": ["Endpoints"],
        "summary": "Update an endpoint",
        "description": "Updates an endpoint. Despite being a `PATCH`, `path` and `method` are **both required** on every call, and the optional fields are replaced rather than merged: any of `delay`, `headers`, `bodyExample`, `showInDoc`, `returnRandomResponse` and `proxyConfiguration` that you leave out is reset to its default. Read the endpoint with `getEndpoint` first and send back the values you want to keep.\n\nThe new `path` + `method` pair must not collide with another endpoint in the same project.",
        "security": [{ "projectApiKey": [] }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/UpdateEndpointRequest" },
              "example": {
                "path": "/users",
                "method": "GET",
                "delay": 500,
                "headers": [{ "key": "content-type", "value": "application/json" }],
                "showInDoc": true,
                "returnRandomResponse": false,
                "proxyConfiguration": "default"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The endpoint after the update.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Endpoint" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      },
      "delete": {
        "operationId": "deleteEndpoint",
        "tags": ["Endpoints"],
        "summary": "Delete an endpoint",
        "description": "Deletes an endpoint and all of its mock responses. The mock server stops answering that `path` + `method` immediately. Frees a slot against the free plan's limit of 4 endpoints per project.",
        "security": [{ "projectApiKey": [] }],
        "responses": {
          "200": {
            "description": "The endpoint was deleted. The body is an empty object.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/EmptyObject" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/public/endpoints/{endpointId}/responses": {
      "parameters": [{ "$ref": "#/components/parameters/EndpointId" }],
      "post": {
        "operationId": "createResponse",
        "tags": ["Responses"],
        "summary": "Create a mock response",
        "description": "Adds a mock response to an endpoint. `status` and `body` are both required.\n\nWhen the endpoint serves JSON (the default), `body` must be a JSON object or array — not a string containing JSON. `name` is a human label shown in the dashboard, capped at 100 characters. The first response created on an endpoint automatically becomes its default response.\n\nAn endpoint can hold several responses and pick between them with `rules`; you can pass them here or set them later with `replaceResponseRules`. Free plans allow **2 responses per endpoint**, and the third one fails with `429`.",
        "security": [{ "projectApiKey": [] }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/CreateResponseRequest" },
              "example": {
                "name": "Success",
                "status": 200,
                "body": { "users": [] },
                "isEnabled": true,
                "rules": []
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The response was created.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/MockResponse" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "429": { "$ref": "#/components/responses/PlanLimitExceeded" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/public/endpoints/{endpointId}/responses/{responseId}": {
      "parameters": [
        { "$ref": "#/components/parameters/EndpointId" },
        { "$ref": "#/components/parameters/ResponseId" }
      ],
      "patch": {
        "operationId": "updateResponse",
        "tags": ["Responses"],
        "summary": "Update a mock response",
        "description": "Updates the payload a mock response serves. Despite being a `PATCH`, `status` and `body` are **both required** on every call, and `name` is replaced with whatever you send — omitting it clears it. `isEnabled` is the one field that keeps its current value when omitted.\n\nRules are not touched here; use `replaceResponseRules` for those. Every change to `body` is kept in the response's `bodyHistory` (last 5 versions), so edits made this way are recoverable from the dashboard.",
        "security": [{ "projectApiKey": [] }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/UpdateResponseRequest" },
              "example": {
                "name": "Success",
                "status": 200,
                "body": { "users": [{ "id": 1, "name": "Ada" }] },
                "isEnabled": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The response after the update.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/MockResponse" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      },
      "delete": {
        "operationId": "deleteResponse",
        "tags": ["Responses"],
        "summary": "Delete a mock response",
        "description": "Removes a mock response from an endpoint. Frees a slot against the free plan's limit of 2 responses per endpoint.",
        "security": [{ "projectApiKey": [] }],
        "responses": {
          "200": {
            "description": "The response was deleted. The body is an empty object.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/EmptyObject" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/public/endpoints/{endpointId}/responses/{responseId}/duplicate": {
      "parameters": [
        { "$ref": "#/components/parameters/EndpointId" },
        { "$ref": "#/components/parameters/ResponseId" }
      ],
      "post": {
        "operationId": "duplicateResponse",
        "tags": ["Responses"],
        "summary": "Duplicate a mock response",
        "description": "Copies an existing response — its `status`, `body`, `isEnabled` flag and `rules` — into a new response on the same endpoint, under the `name` you provide. Useful for deriving an error variant from a working response without rebuilding the body by hand.\n\n`name` is required and capped at 100 characters. The duplicate counts against the free plan's limit of **2 responses per endpoint**, so duplicating the second one fails with `429`.",
        "security": [{ "projectApiKey": [] }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/DuplicateResponseRequest" },
              "example": { "name": "Not found variant" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The new response, copied from the original.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/MockResponse" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": {
            "description": "The response exists but does not belong to this endpoint.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" },
                "example": { "error": "Error: Response not found in this endpoint" }
              }
            }
          },
          "429": { "$ref": "#/components/responses/PlanLimitExceeded" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/public/endpoints/{endpointId}/responses/{responseId}/rules": {
      "parameters": [
        { "$ref": "#/components/parameters/EndpointId" },
        { "$ref": "#/components/parameters/ResponseId" }
      ],
      "put": {
        "operationId": "replaceResponseRules",
        "tags": ["Responses"],
        "summary": "Replace the conditional rules of a response",
        "description": "Sets the conditions under which the endpoint serves this particular response, letting one endpoint answer differently depending on the incoming request.\n\nThis is a full replacement, not a merge: the `rules` array you send becomes the response's complete rule set, and sending `[]` clears every rule. The whole array is validated before anything is stored, so an invalid rule leaves the existing ones untouched.\n\nA rule matches a `property` from a `source` of the request against a `value` using a `comparator`. Add `andConditions` to a rule to require several conditions at once, or send a rule group (`operator` plus `conditions`) to mix `and` and `or`, nested up to 3 levels deep.",
        "security": [{ "projectApiKey": [] }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/ReplaceRulesRequest" },
              "example": {
                "rules": [
                  {
                    "source": "queryString",
                    "property": "page",
                    "comparator": "equal",
                    "value": "2",
                    "andConditions": []
                  },
                  {
                    "source": "body",
                    "property": "user.name",
                    "comparator": "equal",
                    "value": "sergio",
                    "andConditions": [
                      {
                        "source": "body",
                        "property": "user.country",
                        "comparator": "equal",
                        "value": "spain"
                      }
                    ]
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The response with its new rule set.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/MockResponse" }
              }
            }
          },
          "400": {
            "description": "A rule is invalid (unknown `source` or `comparator`, malformed `property`, missing `value`, groups nested too deep), the `rules` field is missing, or the response was not found. Nothing was modified.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" },
                "example": {
                  "error": "Error: Invalid source. Choose someone of this sources: body, queryString, urlParam, header, jsonPath, xmlTag, xPath"
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/hub/catalog": {
      "get": {
        "operationId": "getHubCatalog",
        "tags": ["API Hub"],
        "summary": "List the free public mock APIs",
        "description": "Returns the catalog of Mockfly's API Hub: ready-to-use public mock APIs (users, products, posts and more) that anyone can call without signing up. Each entry describes one API — its `baseUrl`, how many items it holds, an `example` item, every operation it exposes and the query parameters it understands.\n\nThis is the operation to call first when you want to discover the hub: the paths in each entry's `endpoints` are relative to `https://api.mockfly.dev`, so `/hub/users` is directly callable.\n\n**No authentication.** This endpoint is open, needs no API key, and is rate limited by client IP.",
        "security": [],
        "responses": {
          "200": {
            "description": "The catalog of available public mock APIs.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["results"],
                  "properties": {
                    "results": {
                      "type": "array",
                      "description": "One entry per public mock API.",
                      "items": { "$ref": "#/components/schemas/HubApi" }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests from this IP address.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" },
                "example": { "error": "Too many requests, please try again later." }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "accountApiKey": {
        "type": "apiKey",
        "in": "header",
        "name": "Authorization",
        "description": "Your **account** API key, sent as the raw value of the `Authorization` header with no `Bearer` prefix — for example `Authorization: 8f14e45f-ceea-467a-9f0a-1e3b2c4d5e6f`.\n\nUse it for the account-level project operations (`/public/projects`), which have to work before any project exists. Create and revoke account API keys at [app.mockfly.dev/api-keys](https://app.mockfly.dev/api-keys); the full key is shown only once, at creation time."
      },
      "projectApiKey": {
        "type": "apiKey",
        "in": "header",
        "name": "Authorization",
        "description": "The **project** API key of the project you are working on, sent as the raw value of the `Authorization` header with no `Bearer` prefix.\n\nUse it for every endpoint and response operation (`/public/endpoints`): the key is what identifies the project, so those operations take no project parameter. Find it at [app.mockfly.dev](https://app.mockfly.dev) under your project's configuration button, or read it from the `privateApiKey` field returned by `createProject`."
      }
    },
    "parameters": {
      "ProjectId": {
        "name": "projectId",
        "in": "path",
        "required": true,
        "description": "Id of the project, as returned in the `_id` field by `createProject` or `listProjects`.",
        "schema": {
          "type": "string",
          "pattern": "^[0-9a-fA-F]{24}$",
          "description": "A 24-character hexadecimal id."
        },
        "example": "665f1c2e8b3a4c0012ab34cd"
      },
      "EndpointId": {
        "name": "endpointId",
        "in": "path",
        "required": true,
        "description": "Id of the endpoint, as returned in the `_id` field by `listEndpoints` or `createEndpoint`.",
        "schema": {
          "type": "string",
          "pattern": "^[0-9a-fA-F]{24}$",
          "description": "A 24-character hexadecimal id."
        },
        "example": "665f1c2e8b3a4c0012ab34ce"
      },
      "ResponseId": {
        "name": "responseId",
        "in": "path",
        "required": true,
        "description": "Id of the mock response, as returned in the `_id` field by `createResponse` or by the expanded `responses` of `getEndpoint`.",
        "schema": {
          "type": "string",
          "pattern": "^[0-9a-fA-F]{24}$",
          "description": "A 24-character hexadecimal id."
        },
        "example": "665f1c2e8b3a4c0012ab34cf"
      }
    },
    "responses": {
      "BadRequest": {
        "description": "The payload is invalid (a required field is missing, or a value such as `method`, `delay` or `status` is not accepted), or the referenced project, endpoint or response does not exist.",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" },
            "examples": {
              "missingField": {
                "summary": "A required body field was not sent",
                "value": { "error": "name is required" }
              },
              "notFound": {
                "summary": "The referenced resource does not exist",
                "value": { "error": "Error: Project not found" }
              }
            }
          }
        }
      },
      "Unauthorized": {
        "description": "Authentication failed. When the `Authorization` header is missing entirely the body is the plain text `Forbidden`; when the key is present but invalid or revoked the body is the usual JSON error object.",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" },
            "example": { "error": "Error: Invalid API key" }
          },
          "text/plain": {
            "schema": { "type": "string" },
            "example": "Forbidden"
          }
        }
      },
      "Forbidden": {
        "description": "You can access the project but you are not its admin, and only the admin may perform this operation.",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" },
            "example": { "error": "Error: Only the admin can update the project." }
          }
        }
      },
      "PlanLimitExceeded": {
        "description": "A limit of your subscription plan was reached. On the free plan: 1 project as admin, 4 endpoints per project and 2 responses per endpoint. Delete something to free a slot, or [upgrade to premium](https://app.mockfly.dev/upgrade-plan) to remove the limits.",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" },
            "example": {
              "error": "You have exceeded the maximum number of endpoints you can create for you subscription plan."
            }
          }
        }
      },
      "InternalError": {
        "description": "The request could not be completed. Besides genuine server errors, the endpoint and response operations also answer `500` when the project API key is invalid or revoked, and when a validation error is raised by the operation itself (an unsupported `method`, a duplicated `path` + `method` pair, an endpoint or response that does not exist). Read the `error` message to tell them apart.",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" },
            "example": { "error": "Error: Project not found" }
          }
        }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "title": "Error",
        "description": "Every error is returned as a JSON object with a single, human-readable `error` message. There is no machine-readable error code — branch on the HTTP status, and use the message only for logging or display.",
        "required": ["error"],
        "properties": {
          "error": {
            "type": "string",
            "description": "Human-readable description of what went wrong.",
            "examples": ["Error: Project not found", "name is required"]
          }
        }
      },
      "EmptyObject": {
        "type": "object",
        "title": "Empty object",
        "description": "An empty JSON object, returned by the delete operations that have nothing to report.",
        "examples": [{}]
      },
      "Tag": {
        "type": "object",
        "title": "Tag",
        "description": "A colored label used to organise projects in the dashboard.",
        "properties": {
          "name": {
            "type": "string",
            "description": "Label text.",
            "examples": ["backend"]
          },
          "color": {
            "type": "string",
            "description": "Hex color used to render the label.",
            "examples": ["#48cfad"]
          }
        }
      },
      "ProjectSummary": {
        "type": "object",
        "title": "Project summary",
        "description": "The compact project shape returned by `listProjects`.",
        "properties": {
          "_id": {
            "type": "string",
            "description": "Project id, to be used as the `projectId` path parameter.",
            "examples": ["665f1c2e8b3a4c0012ab34cd"]
          },
          "name": {
            "type": "string",
            "description": "Project name.",
            "examples": ["My project"]
          },
          "slug": {
            "type": "string",
            "description": "Unique slug of the project. It is the subdomain of its mock server: `https://<slug>.mockfly.dev`.",
            "examples": ["0f9a7c1e-4b3d-4f19-9a2e-6c8d5b1f7a30"]
          }
        }
      },
      "Project": {
        "type": "object",
        "title": "Project",
        "description": "A Mockfly project: the container for a set of endpoints and the mock server that serves them.",
        "properties": {
          "_id": {
            "type": "string",
            "description": "Project id, to be used as the `projectId` path parameter.",
            "examples": ["665f1c2e8b3a4c0012ab34cd"]
          },
          "name": {
            "type": "string",
            "description": "Project name.",
            "examples": ["My project"]
          },
          "slug": {
            "type": "string",
            "description": "Unique slug of the project. It is the subdomain of its mock server: `https://<slug>.mockfly.dev`.",
            "examples": ["0f9a7c1e-4b3d-4f19-9a2e-6c8d5b1f7a30"]
          },
          "privateApiKey": {
            "type": ["string", "null"],
            "description": "The project API key, which authenticates every endpoint and response operation. Treat it as a secret. It is returned when the project is created or updated, and is `null` in the projects nested inside endpoint responses.",
            "examples": ["3c1d9f28-7a4b-4c6e-8f01-2b5d7e9a4c13"]
          },
          "tags": {
            "type": "array",
            "description": "Labels attached to the project.",
            "items": { "$ref": "#/components/schemas/Tag" }
          },
          "admin": {
            "description": "The account that administers the project — the only one allowed to update or delete it. Returned as a populated user object.",
            "type": ["object", "string", "null"]
          },
          "allowedUsers": {
            "type": "array",
            "description": "The accounts with access to the project, as populated user objects.",
            "items": { "type": ["object", "string"] }
          },
          "useProxy": {
            "type": "boolean",
            "description": "Whether the project falls back to proxying the real API instead of serving mocks."
          },
          "proxyUrl": {
            "type": "string",
            "description": "Base URL the project proxies to when `useProxy` is enabled."
          },
          "numberOfRequests": {
            "type": "integer",
            "description": "Requests served by the mock server in the current billing period."
          },
          "totalNumberOfRequests": {
            "type": "integer",
            "description": "Requests served by the mock server since the project was created."
          }
        }
      },
      "EndpointHeader": {
        "type": "object",
        "title": "Endpoint header",
        "description": "A header the mock server adds to every response of the endpoint. A `content-type` header is what switches the endpoint between JSON, XML, PDF and CSV.",
        "properties": {
          "key": {
            "type": "string",
            "description": "Header name.",
            "examples": ["content-type"]
          },
          "value": {
            "type": "string",
            "description": "Header value. For `content-type`, only `application/json`, `application/xml`, `text/xml`, `text/xml; charset=utf-8`, `application/pdf` and `text/csv` are accepted.",
            "examples": ["application/json"]
          }
        }
      },
      "HttpMethod": {
        "type": "string",
        "title": "HTTP method",
        "description": "The HTTP method the mocked endpoint answers to.",
        "enum": ["GET", "PUT", "POST", "PATCH", "DELETE", "OPTIONS", "HEAD"]
      },
      "ProxyConfiguration": {
        "type": "string",
        "title": "Proxy configuration",
        "description": "How this endpoint resolves between the mock and the real API: `default` follows the project setting, `useProxy` always forwards to the real API, `useMock` always serves the mock.",
        "enum": ["default", "useProxy", "useMock"],
        "default": "default"
      },
      "Endpoint": {
        "type": "object",
        "title": "Endpoint",
        "description": "A mocked route inside a project, identified by its `path` and `method`.",
        "properties": {
          "_id": {
            "type": "string",
            "description": "Endpoint id, to be used as the `endpointId` path parameter.",
            "examples": ["665f1c2e8b3a4c0012ab34ce"]
          },
          "path": {
            "type": "string",
            "description": "Path served by the mock, optionally with URL parameters written with a colon.",
            "examples": ["/users", "/users/:id"]
          },
          "method": { "$ref": "#/components/schemas/HttpMethod" },
          "description": {
            "type": "string",
            "description": "Free-text description, shown in the auto-generated documentation. Omitted from `listEndpoints`."
          },
          "delay": {
            "type": "integer",
            "description": "Milliseconds the mock server waits before answering, for simulating latency.",
            "minimum": 0,
            "default": 0
          },
          "headers": {
            "type": "array",
            "description": "Headers added to every response of this endpoint.",
            "items": { "$ref": "#/components/schemas/EndpointHeader" }
          },
          "bodyExample": {
            "type": ["object", "array"],
            "description": "Example request body, shown in the auto-generated documentation. Must be a JSON object or array when the endpoint serves JSON. Omitted from `listEndpoints`."
          },
          "showInDoc": {
            "type": "boolean",
            "description": "Whether the endpoint appears in the project's auto-generated documentation.",
            "default": true
          },
          "returnRandomResponse": {
            "type": "boolean",
            "description": "When enabled, the mock server picks one of the endpoint's responses at random and conditional rules are ignored.",
            "default": false
          },
          "proxyConfiguration": { "$ref": "#/components/schemas/ProxyConfiguration" },
          "position": {
            "type": "integer",
            "description": "Sort order of the endpoint in the dashboard.",
            "default": 0
          },
          "project": {
            "description": "The project the endpoint belongs to, as a populated project object whose `privateApiKey` is always `null`.",
            "type": ["object", "string"]
          },
          "responses": {
            "type": "array",
            "description": "The mock responses of the endpoint. Returned as ids by `listEndpoints`, and as full objects by `getEndpoint`.",
            "items": { "type": ["object", "string"] }
          },
          "defaultResponse": {
            "description": "The response served when no conditional rule matches. Omitted from `listEndpoints`.",
            "type": ["object", "string", "null"]
          },
          "totalNumberOfRequests": {
            "type": "integer",
            "description": "Requests served by this endpoint since it was created."
          }
        }
      },
      "EndpointDetail": {
        "title": "Endpoint detail",
        "description": "An endpoint with its `responses` expanded into full objects, as returned by `getEndpoint`.",
        "allOf": [
          { "$ref": "#/components/schemas/Endpoint" },
          {
            "type": "object",
            "properties": {
              "responses": {
                "type": "array",
                "description": "The mock responses of the endpoint, fully expanded.",
                "items": { "$ref": "#/components/schemas/MockResponse" }
              }
            }
          }
        ]
      },
      "MockResponse": {
        "type": "object",
        "title": "Mock response",
        "description": "One of the payloads an endpoint can serve. An endpoint may hold several, and picks between them with conditional `rules`.",
        "properties": {
          "_id": {
            "type": "string",
            "description": "Response id, to be used as the `responseId` path parameter.",
            "examples": ["665f1c2e8b3a4c0012ab34cf"]
          },
          "name": {
            "type": "string",
            "description": "Human label shown in the dashboard.",
            "maxLength": 100,
            "examples": ["Success"]
          },
          "status": {
            "type": "integer",
            "description": "HTTP status code the mock server answers with.",
            "examples": [200, 404]
          },
          "body": {
            "type": ["object", "array"],
            "description": "The payload served to the caller. A JSON object or array when the endpoint serves JSON.",
            "examples": [{ "users": [] }]
          },
          "isEnabled": {
            "type": "boolean",
            "description": "Whether the mock server may serve this response at all.",
            "default": true
          },
          "rules": {
            "type": "array",
            "description": "Conditions under which this response is served. An empty array means it is unconditional.",
            "items": { "$ref": "#/components/schemas/RuleNode" }
          },
          "bodyHistory": {
            "type": "array",
            "description": "The last 5 versions of `body`, newest last, so an edit can be rolled back from the dashboard.",
            "items": {
              "type": "object",
              "properties": {
                "body": {
                  "type": ["object", "array"],
                  "description": "The payload as it was before being replaced."
                },
                "date": {
                  "type": "string",
                  "format": "date-time",
                  "description": "When this version was replaced."
                },
                "user": {
                  "type": ["object", "string", "null"],
                  "description": "The account that made the change."
                }
              }
            }
          }
        }
      },
      "RuleSource": {
        "type": "string",
        "title": "Rule source",
        "description": "Which part of the incoming request a rule reads: `body` a property of the JSON body (dot notation, `user.name`), `queryString` a query parameter, `urlParam` a URL parameter declared in the path (`:id`), `header` a request header, `jsonPath` a JSONPath expression over the body (`$.items[0].sku`), `xmlTag` the presence or value of an XML tag, `xPath` an XPath expression over an XML body.",
        "enum": ["body", "queryString", "urlParam", "header", "jsonPath", "xmlTag", "xPath"]
      },
      "RuleComparator": {
        "type": "string",
        "title": "Rule comparator",
        "description": "How the extracted value is compared against `value`. `exists`, `notExists`, `isEmpty` and `isNotEmpty` need no `value`; `greaterThan`, `greaterOrEqual`, `lessThan` and `lessOrEqual` need a numeric one; `regex` needs a valid regular expression. The `xmlTag` and `xPath` sources only accept `equal` and `distinct`.",
        "enum": [
          "equal",
          "distinct",
          "includes",
          "contains",
          "notContains",
          "startsWith",
          "endsWith",
          "regex",
          "greaterThan",
          "greaterOrEqual",
          "lessThan",
          "lessOrEqual",
          "exists",
          "notExists",
          "isEmpty",
          "isNotEmpty"
        ]
      },
      "Rule": {
        "type": "object",
        "title": "Rule",
        "description": "A single condition on the incoming request. All the entries of `andConditions` must also hold for the rule to match.",
        "required": ["source", "comparator"],
        "properties": {
          "source": { "$ref": "#/components/schemas/RuleSource" },
          "property": {
            "type": "string",
            "description": "The property to read from `source`. Dot notation for `body` (`user.name`), a JSONPath expression for `jsonPath`, the parameter or header name for `queryString`, `urlParam` and `header`. May be empty for `xmlTag`, where `value` carries the tag name.",
            "examples": ["user.name", "page"]
          },
          "comparator": { "$ref": "#/components/schemas/RuleComparator" },
          "value": {
            "type": "string",
            "description": "The value to compare against. Not needed for the comparators that take none, and must be numeric for the numeric comparators.",
            "examples": ["sergio", "2"]
          },
          "andConditions": {
            "type": "array",
            "description": "Extra conditions that must all hold together with this one.",
            "items": { "$ref": "#/components/schemas/Rule" }
          },
          "position": {
            "type": "integer",
            "description": "Evaluation order of the rule."
          }
        }
      },
      "RuleGroup": {
        "type": "object",
        "title": "Rule group",
        "description": "A group of conditions combined with a single operator, so that `and` and `or` can be mixed. Groups may nest up to 3 levels deep.",
        "required": ["operator", "conditions"],
        "properties": {
          "operator": {
            "type": "string",
            "description": "How the entries of `conditions` are combined.",
            "enum": ["and", "or"]
          },
          "conditions": {
            "type": "array",
            "description": "The conditions of the group. Must hold at least one, and each entry may itself be a rule or another group.",
            "minItems": 1,
            "items": { "$ref": "#/components/schemas/RuleNode" }
          }
        }
      },
      "RuleNode": {
        "title": "Rule or rule group",
        "description": "Either a single rule or a group of them. An entry is treated as a group when it carries a `conditions` array.",
        "oneOf": [{ "$ref": "#/components/schemas/Rule" }, { "$ref": "#/components/schemas/RuleGroup" }]
      },
      "CreateProjectRequest": {
        "type": "object",
        "title": "Create project request",
        "required": ["name"],
        "properties": {
          "name": {
            "type": "string",
            "description": "Name of the new project.",
            "examples": ["My project"]
          },
          "tags": {
            "type": "array",
            "description": "Optional labels to organise the project in the dashboard.",
            "items": { "$ref": "#/components/schemas/Tag" }
          }
        }
      },
      "UpdateProjectRequest": {
        "type": "object",
        "title": "Update project request",
        "description": "Both fields are optional; the ones you send replace the current values.",
        "properties": {
          "name": {
            "type": "string",
            "description": "New name for the project.",
            "examples": ["Renamed project"]
          },
          "tags": {
            "type": "array",
            "description": "The complete new list of labels. It replaces the existing ones rather than adding to them.",
            "items": { "$ref": "#/components/schemas/Tag" }
          }
        }
      },
      "ImportEndpointResponse": {
        "type": "object",
        "title": "Imported response",
        "description": "A mock response inside an imported endpoint.",
        "properties": {
          "name": {
            "type": "string",
            "description": "Human label for the response.",
            "maxLength": 100,
            "examples": ["Success"]
          },
          "status": {
            "type": "integer",
            "description": "HTTP status code to answer with.",
            "examples": [200]
          },
          "body": {
            "type": ["object", "array"],
            "description": "The payload to serve. Must be a JSON object or array — not a string containing JSON — when the endpoint serves JSON."
          },
          "isEnabled": {
            "type": "boolean",
            "description": "Whether the mock server may serve this response.",
            "default": true
          },
          "rules": {
            "type": "array",
            "description": "Conditions under which this response is served.",
            "items": { "$ref": "#/components/schemas/RuleNode" }
          }
        }
      },
      "ImportEndpoint": {
        "type": "object",
        "title": "Imported endpoint",
        "description": "An endpoint to create as part of an import. `path`, `method` and `responses` are required, though `responses` may be empty.",
        "required": ["path", "method", "responses"],
        "properties": {
          "path": {
            "type": "string",
            "description": "Path to mock, optionally with URL parameters written with a colon.",
            "examples": ["/users", "/products/:id"]
          },
          "method": { "$ref": "#/components/schemas/HttpMethod" },
          "responses": {
            "type": "array",
            "description": "The mock responses of the endpoint. An empty array is allowed.",
            "items": { "$ref": "#/components/schemas/ImportEndpointResponse" }
          },
          "description": {
            "type": "string",
            "description": "Free-text description for the auto-generated documentation."
          },
          "delay": {
            "type": "integer",
            "description": "Milliseconds to wait before answering.",
            "minimum": 0,
            "default": 0
          },
          "headers": {
            "type": "array",
            "description": "Headers added to every response of the endpoint.",
            "items": { "$ref": "#/components/schemas/EndpointHeader" }
          },
          "bodyExample": {
            "type": ["object", "array"],
            "description": "Example request body for the documentation. Must be a JSON object or array when the endpoint serves JSON."
          },
          "showInDoc": {
            "type": "boolean",
            "description": "Whether the endpoint appears in the auto-generated documentation.",
            "default": true
          },
          "returnRandomResponse": {
            "type": "boolean",
            "description": "Serve a random response instead of evaluating rules.",
            "default": false
          },
          "proxyConfiguration": { "$ref": "#/components/schemas/ProxyConfiguration" },
          "position": {
            "type": "integer",
            "description": "Sort order in the dashboard.",
            "default": 0
          }
        }
      },
      "ImportProjectRequest": {
        "type": "object",
        "title": "Import project request",
        "description": "The dashboard's \"Export to JSON\" payload. Only `project.name` is strictly required, so the same document can be used to seed an empty project or a fully populated one.",
        "required": ["project"],
        "properties": {
          "project": {
            "type": "object",
            "description": "The project to create.",
            "required": ["name"],
            "properties": {
              "name": {
                "type": "string",
                "description": "Name of the new project.",
                "examples": ["Imported project"]
              }
            }
          },
          "endpoints": {
            "type": "array",
            "description": "The endpoints to create, each with its own responses. May be omitted or empty.",
            "items": { "$ref": "#/components/schemas/ImportEndpoint" }
          },
          "folders": {
            "type": "array",
            "description": "Optional folders used to group the imported endpoints in the dashboard.",
            "items": {
              "type": "object",
              "properties": {
                "name": {
                  "type": "string",
                  "description": "Folder name."
                },
                "endpointIds": {
                  "type": "array",
                  "description": "The endpoints of the folder, referenced by the ids used in the export payload.",
                  "items": { "type": "string" }
                }
              }
            }
          },
          "environment": {
            "type": "object",
            "description": "Optional environment variables of the project, as a flat map of name to value.",
            "additionalProperties": { "type": "string" }
          }
        }
      },
      "CreateEndpointRequest": {
        "type": "object",
        "title": "Create endpoint request",
        "required": ["projectId", "path", "method"],
        "properties": {
          "projectId": {
            "type": "string",
            "description": "Id of the project to add the endpoint to. It must be the project the API key belongs to.",
            "pattern": "^[0-9a-fA-F]{24}$",
            "examples": ["665f1c2e8b3a4c0012ab34cd"]
          },
          "path": {
            "type": "string",
            "description": "Path to mock, optionally with URL parameters written with a colon. It must not collide with another endpoint of the same project using the same method.",
            "examples": ["/users", "/users/:id"]
          },
          "method": { "$ref": "#/components/schemas/HttpMethod" },
          "description": {
            "type": "string",
            "description": "Free-text description for the auto-generated documentation."
          },
          "delay": {
            "type": "integer",
            "description": "Milliseconds to wait before answering.",
            "minimum": 0,
            "default": 0
          },
          "headers": {
            "type": "array",
            "description": "Headers added to every response of the endpoint.",
            "items": { "$ref": "#/components/schemas/EndpointHeader" }
          },
          "bodyExample": {
            "type": ["object", "array"],
            "description": "Example request body for the documentation. Must be a JSON object or array when the endpoint serves JSON."
          },
          "position": {
            "type": "integer",
            "description": "Sort order in the dashboard.",
            "default": 0
          }
        }
      },
      "UpdateEndpointRequest": {
        "type": "object",
        "title": "Update endpoint request",
        "description": "`path` and `method` are required on every call. Every other field is reset to its default when omitted, so send the values you want to keep.",
        "required": ["path", "method"],
        "properties": {
          "path": {
            "type": "string",
            "description": "Path to mock. It must not collide with another endpoint of the same project using the same method.",
            "examples": ["/users"]
          },
          "method": { "$ref": "#/components/schemas/HttpMethod" },
          "description": {
            "type": "string",
            "description": "Free-text description for the auto-generated documentation. This is the one field left untouched when omitted."
          },
          "delay": {
            "type": "integer",
            "description": "Milliseconds to wait before answering. Reset to 0 when omitted.",
            "minimum": 0,
            "default": 0
          },
          "headers": {
            "type": "array",
            "description": "Headers added to every response of the endpoint. Emptied when omitted.",
            "items": { "$ref": "#/components/schemas/EndpointHeader" }
          },
          "bodyExample": {
            "type": ["object", "array"],
            "description": "Example request body for the documentation. Emptied when omitted."
          },
          "showInDoc": {
            "type": "boolean",
            "description": "Whether the endpoint appears in the auto-generated documentation. Reset to `true` when omitted.",
            "default": true
          },
          "returnRandomResponse": {
            "type": "boolean",
            "description": "Serve a random response instead of evaluating rules. Reset to `false` when omitted.",
            "default": false
          },
          "proxyConfiguration": { "$ref": "#/components/schemas/ProxyConfiguration" },
          "defaultResponse": {
            "type": "string",
            "description": "Id of the response to serve when no conditional rule matches.",
            "examples": ["665f1c2e8b3a4c0012ab34cf"]
          }
        }
      },
      "CreateResponseRequest": {
        "type": "object",
        "title": "Create response request",
        "required": ["status", "body"],
        "properties": {
          "status": {
            "type": "integer",
            "description": "HTTP status code the mock server answers with. Must be a valid HTTP status code.",
            "examples": [200, 404]
          },
          "body": {
            "type": ["object", "array"],
            "description": "The payload to serve. Must be a JSON object or array — not a string containing JSON — when the endpoint serves JSON.",
            "examples": [{ "users": [] }]
          },
          "name": {
            "type": "string",
            "description": "Human label shown in the dashboard.",
            "maxLength": 100,
            "examples": ["Success"]
          },
          "isEnabled": {
            "type": "boolean",
            "description": "Whether the mock server may serve this response.",
            "default": true
          },
          "rules": {
            "type": "array",
            "description": "Conditions under which this response is served. Can also be set later with `replaceResponseRules`.",
            "items": { "$ref": "#/components/schemas/RuleNode" }
          }
        }
      },
      "UpdateResponseRequest": {
        "type": "object",
        "title": "Update response request",
        "description": "`status` and `body` are required on every call. `name` is replaced with whatever you send, so omitting it clears it; `isEnabled` keeps its current value when omitted.",
        "required": ["status", "body"],
        "properties": {
          "status": {
            "type": "integer",
            "description": "HTTP status code the mock server answers with. Must be a valid HTTP status code.",
            "examples": [200]
          },
          "body": {
            "type": ["object", "array"],
            "description": "The payload to serve. Must be a JSON object or array when the endpoint serves JSON."
          },
          "name": {
            "type": "string",
            "description": "Human label shown in the dashboard. Cleared when omitted.",
            "maxLength": 100
          },
          "isEnabled": {
            "type": "boolean",
            "description": "Whether the mock server may serve this response. Left unchanged when omitted."
          }
        }
      },
      "DuplicateResponseRequest": {
        "type": "object",
        "title": "Duplicate response request",
        "required": ["name"],
        "properties": {
          "name": {
            "type": "string",
            "description": "Human label for the new copy.",
            "maxLength": 100,
            "examples": ["Not found variant"]
          }
        }
      },
      "ReplaceRulesRequest": {
        "type": "object",
        "title": "Replace rules request",
        "required": ["rules"],
        "properties": {
          "rules": {
            "type": "array",
            "description": "The complete new rule set for the response. It replaces the existing one; send `[]` to remove every rule.",
            "items": { "$ref": "#/components/schemas/RuleNode" }
          }
        }
      },
      "HubApi": {
        "type": "object",
        "title": "Hub API",
        "description": "One of the free public mock APIs of the API Hub.",
        "properties": {
          "slug": {
            "type": "string",
            "description": "Identifier of the API, also its page on mockfly.dev: `https://mockfly.dev/api-hub/<slug>/`.",
            "examples": ["users"]
          },
          "name": {
            "type": "string",
            "description": "Display name of the API.",
            "examples": ["Users"]
          },
          "description": {
            "type": "string",
            "description": "Short description of what the API serves.",
            "examples": ["A classic users API with profile, address and company data."]
          },
          "seoDescription": {
            "type": "string",
            "description": "Longer description used as the meta description of the API's page."
          },
          "baseUrl": {
            "type": "string",
            "description": "Base path of the API, relative to `https://api.mockfly.dev`.",
            "examples": ["/hub/users"]
          },
          "total": {
            "type": "integer",
            "description": "How many items the collection holds. Item ids run from 1 to this number.",
            "examples": [100]
          },
          "example": {
            "type": ["object", "array"],
            "description": "A sample item, showing the shape the API returns."
          },
          "endpoints": {
            "type": "array",
            "description": "Every operation the API exposes.",
            "items": {
              "type": "object",
              "properties": {
                "method": { "$ref": "#/components/schemas/HttpMethod" },
                "path": {
                  "type": "string",
                  "description": "Path of the operation, relative to `https://api.mockfly.dev`, with URL parameters written with a colon.",
                  "examples": ["/hub/users/:id"]
                },
                "description": {
                  "type": "string",
                  "description": "What the operation does.",
                  "examples": ["Get a single user by id (1-100)"]
                }
              }
            }
          },
          "queryParams": {
            "type": "array",
            "description": "The query parameters the API understands, for pagination and for simulating latency and errors.",
            "items": {
              "type": "object",
              "properties": {
                "name": {
                  "type": "string",
                  "description": "Parameter name.",
                  "examples": ["_page", "_limit", "_delay", "_status"]
                },
                "description": {
                  "type": "string",
                  "description": "What the parameter does and its default.",
                  "examples": ["Page number (default 1)"]
                }
              }
            }
          }
        }
      }
    }
  }
}
