{
  "openapi": "3.1.0",
  "info": {
    "title": "FrankSpace Public API",
    "version": "0.4.0",
    "summary": "Read-only search over live UK office and workspace listings.",
    "description": "An OpenAPI mirror of the FrankSpace MCP tools, for agents and clients that consume OpenAPI rather than MCP. The canonical interface is the MCP server at https://bqixfybgflusxwnvzjwu.supabase.co/functions/v1/mcp — every operation here maps 1:1 to an MCP tool of the same name. The search operations are read-only over marketplace data; the only write is submit_feedback, which files a note for the FrankSpace team and is not a contact channel. price_monthly is the TOTAL monthly price for the space in integer pence, whatever pricing_mode says. Workspace identifiers are UUIDs, and the public page for one is https://frankspace.co.uk/office/{id}.",
    "contact": {
      "name": "FrankSpace",
      "url": "https://frankspace.co.uk",
      "email": "enquiries@frankspace.co.uk"
    },
    "license": {
      "name": "Proprietary",
      "url": "https://frankspace.co.uk/legal"
    }
  },
  "servers": [
    {
      "url": "https://bqixfybgflusxwnvzjwu.supabase.co/functions/v1",
      "description": "Public MCP endpoint. Operations below are exposed as MCP tools over streamable-http."
    }
  ],
  "externalDocs": {
    "description": "Agent skill — how to query FrankSpace correctly",
    "url": "https://frankspace.co.uk/skill.md"
  },
  "paths": {
    "/mcp#ai_search": {
      "post": {
        "operationId": "ai_search",
        "summary": "Natural-language workspace search",
        "description": "Search FrankSpace with a plain-English query — the same parser that powers the site's search bar. Handles budgets, headcount, neighbourhoods, amenities, EPC ratings and lease terms. Prefer this over search_workspaces whenever the user supplied a sentence.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "query",
                  "agent_key"
                ],
                "properties": {
                  "query": {
                    "type": "string",
                    "minLength": 2,
                    "description": "Plain-English query, e.g. 'small office for 6 people in Shoreditch under £3k'.",
                    "examples": [
                      "6-person office in Shoreditch under £3k a month with 24/7 access"
                    ]
                  },
                  "client_hint": {
                    "type": "string",
                    "description": "Name of the calling agent (e.g. 'claude', 'chatgpt', 'cursor') for usage attribution."
                  },
                  "agent_key": {
                    "type": "string",
                    "description": "Your FrankSpace agent key. Obtain one with register_agent."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Parsed filters and matching listings.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "filters": {
                      "type": "object",
                      "description": "How the query was parsed. Inspect this before trusting the results."
                    },
                    "total": {
                      "type": "integer"
                    },
                    "results": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/WorkspaceSummary"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, unconfirmed, unrecognised or revoked agent_key. Call register_agent."
          },
          "429": {
            "description": "Rate limited. Back off and retry once; do not loop."
          }
        }
      }
    },
    "/mcp#search_workspaces": {
      "post": {
        "operationId": "search_workspaces",
        "summary": "Structured workspace search",
        "description": "Filter live listings by location text, size band and maximum monthly price. Use when you already have precise structured criteria, or to enumerate an area exhaustively.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "location": {
                    "type": "string",
                    "description": "Free-text location — city, postcode or submarket, e.g. 'Shoreditch' or 'EC2A'. Matched loosely."
                  },
                  "size_band": {
                    "type": "string",
                    "enum": [
                      "small",
                      "medium",
                      "large",
                      "xlarge"
                    ],
                    "description": "small ≤500 sq ft, medium 500–1500, large 1500–3000, xlarge >3000."
                  },
                  "max_price_pence": {
                    "type": "integer",
                    "minimum": 1,
                    "description": "Maximum monthly price in PENCE, not pounds. £3,000 is 300000."
                  },
                  "limit": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 50,
                    "default": 10
                  },
                  "client_hint": {
                    "type": "string",
                    "description": "Calling agent name, for usage attribution."
                  },
                  "agent_key": {
                    "type": "string",
                    "description": "Your FrankSpace agent key. Obtain one with register_agent."
                  }
                },
                "required": [
                  "agent_key"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Matching listings.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "results": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/WorkspaceSummary"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, unconfirmed, unrecognised or revoked agent_key. Call register_agent."
          },
          "429": {
            "description": "Rate limited. Back off and retry once; do not loop."
          }
        }
      }
    },
    "/mcp#get_workspace": {
      "post": {
        "operationId": "get_workspace",
        "summary": "Full public detail for one workspace",
        "description": "Fetch the complete public record for a single listing by UUID, including description, amenities, photos, EPC rating, minimum term, availability and the parent building. Call this before describing a specific workspace in detail.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "workspace_id",
                  "agent_key"
                ],
                "properties": {
                  "workspace_id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Workspace UUID.",
                    "examples": [
                      "ca30f2fd-cc2b-4b81-8852-f55110cc79e7"
                    ]
                  },
                  "client_hint": {
                    "type": "string",
                    "description": "Calling agent name, for usage attribution."
                  },
                  "agent_key": {
                    "type": "string",
                    "description": "Your FrankSpace agent key. Obtain one with register_agent."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Full public workspace record.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "workspace": {
                      "$ref": "#/components/schemas/Workspace"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, unconfirmed, unrecognised or revoked agent_key. Call register_agent."
          },
          "404": {
            "description": "No live workspace with that ID."
          }
        }
      }
    },
    "/mcp#get_frankspace_skill": {
      "post": {
        "operationId": "get_frankspace_skill",
        "summary": "Fetch the FrankSpace Agent Skill",
        "description": "Returns the agent skill published at https://frankspace.co.uk/skill.md: how to query the inventory correctly, how to read the data, the commercial facts, and the limits of what this server can do. For clients that cannot fetch it over HTTP.",
        "security": [],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "client_hint": {
                    "type": "string",
                    "description": "Calling agent name, for usage attribution."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The skill, as markdown.",
            "content": {
              "text/markdown": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/mcp#submit_feedback": {
      "post": {
        "operationId": "submit_feedback",
        "summary": "Report a problem with FrankSpace data or documentation",
        "description": "Report something wrong or missing in the agent-facing surface: a stale or incorrect listing, a field or filter that does not exist, an unclear statement in llms.txt / skill.md, or a misbehaving tool. Reviewed by the FrankSpace team. NOT a contact channel — nobody replies, it reaches no property owner, and it must not carry user enquiries or personal data. Enquiries and viewings happen on the listing page, initiated by the user. Rate limited to a handful per caller per hour.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "message"
                ],
                "properties": {
                  "message": {
                    "type": "string",
                    "minLength": 10,
                    "maxLength": 4000,
                    "description": "What is wrong, in plain English: what you observed and what you expected. No personal data about the user."
                  },
                  "kind": {
                    "type": "string",
                    "enum": [
                      "data_error",
                      "missing_data",
                      "docs_unclear",
                      "tool_problem",
                      "other"
                    ],
                    "default": "other",
                    "description": "data_error: a listing's details are wrong or stale. missing_data: a field or filter you needed does not exist. docs_unclear: the agent documentation is unclear or contradictory. tool_problem: a tool errored or returned something unusable."
                  },
                  "workspace_id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Workspace the report concerns, if it concerns one listing."
                  },
                  "subject_url": {
                    "type": "string",
                    "maxLength": 500,
                    "description": "The FrankSpace URL the report is about, if not a specific workspace."
                  },
                  "client_hint": {
                    "type": "string",
                    "description": "Calling agent name, for usage attribution."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Feedback recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "feedback_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "Reference for the recorded report. There is no way to read it back."
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Back off; do not resubmit in a loop."
          }
        }
      }
    },
    "/mcp#register_agent": {
      "post": {
        "operationId": "register_agent",
        "summary": "Request a free FrankSpace agent key",
        "description": "Request an agent key, which ai_search, search_workspaces and get_workspace require. The key is returned once and is inert until you follow the confirmation link emailed to the address you supply. Keys are free. Register once and reuse the key; registration is rate limited per address.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "contact_email",
                  "org_name",
                  "purpose"
                ],
                "properties": {
                  "contact_email": {
                    "type": "string",
                    "format": "email",
                    "description": "An address you control. The confirmation link is sent here."
                  },
                  "org_name": {
                    "type": "string",
                    "maxLength": 200,
                    "description": "The organisation or product this key is for."
                  },
                  "purpose": {
                    "type": "string",
                    "maxLength": 1000,
                    "description": "What you intend to use FrankSpace listing data for."
                  },
                  "client_hint": {
                    "type": "string",
                    "description": "Calling agent name, for usage attribution."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Key created, pending confirmation. Store the key: it is not shown again.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "agent_key": {
                      "type": "string",
                      "description": "The key. Shown once."
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "pending_confirmation"
                      ]
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many registrations for this address. Reuse the key you already have."
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {},
    "schemas": {
      "WorkspaceSummary": {
        "type": "object",
        "description": "Summary row returned by search. Call get_workspace for the full record.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Public page: https://frankspace.co.uk/office/{id}"
          },
          "title": {
            "type": "string"
          },
          "city": {
            "type": "string"
          },
          "submarket": {
            "type": "string",
            "description": "London submarket, e.g. 'Farringdon'."
          },
          "postcode": {
            "type": "string"
          },
          "size_sqft": {
            "type": "integer",
            "nullable": true
          },
          "size_desks": {
            "type": "integer",
            "nullable": true
          },
          "workspace_type": {
            "type": "string",
            "examples": [
              "Private Office",
              "Co-working"
            ]
          },
          "price_monthly": {
            "type": "integer",
            "nullable": true,
            "description": "TOTAL monthly price for the space, in INTEGER PENCE."
          },
          "price_monthly_gbp": {
            "type": "number",
            "nullable": true,
            "description": "Derived convenience field: the same total, in pounds."
          },
          "pricing_mode": {
            "type": "string",
            "enum": [
              "total",
              "per_desk"
            ],
            "description": "How the owner entered the price when listing (a per-desk entry is multiplied up before saving). It does NOT change price_monthly, which is always the total. Per-desk is price_monthly / size_desks."
          }
        }
      },
      "Workspace": {
        "allOf": [
          {
            "$ref": "#/components/schemas/WorkspaceSummary"
          },
          {
            "type": "object",
            "properties": {
              "description": {
                "type": "string"
              },
              "building_name": {
                "type": "string"
              },
              "address_line1": {
                "type": "string"
              },
              "latitude": {
                "type": "number",
                "nullable": true
              },
              "longitude": {
                "type": "number",
                "nullable": true
              },
              "min_term_months": {
                "type": "integer",
                "nullable": true
              },
              "term_type": {
                "type": "string",
                "nullable": true
              },
              "available_from": {
                "type": "string",
                "format": "date",
                "nullable": true
              },
              "epc_rating": {
                "type": "string",
                "nullable": true,
                "description": "Energy Performance Certificate band A–G."
              },
              "amenities": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "photos": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "updated_at": {
                "type": "string",
                "format": "date-time"
              }
            }
          }
        ]
      }
    }
  }
}
