{
    "openapi": "3.1.0",
    "info": {
        "title": "Apartspace Agent API",
        "version": "1.0.0",
        "description": "Read access to the Apartspace public apartment marketplace for AI agents: search listings, fetch a listing, and price a stay. Bookings are not part of the read tier. An MCP (Model Context Protocol) endpoint exposing the same tools over JSON-RPC is available at POST /api/agent/v1/mcp (same Bearer auth)."
    },
    "servers": [
        {
            "url": "/api/agent/v1"
        }
    ],
    "security": [
        {
            "bearerAuth": []
        }
    ],
    "components": {
        "securitySchemes": {
            "bearerAuth": {
                "type": "http",
                "scheme": "bearer",
                "description": "Machine token in the form \"{credentialId}:{secret}\" issued to your client. Scope: listings:read."
            }
        },
        "schemas": {
            "Money": {
                "type": "object",
                "description": "Amounts are integers in the minor unit (e.g. cents) of `currency`.",
                "properties": {
                    "currency": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "example": "EUR"
                    },
                    "nightly": {
                        "type": [
                            "integer",
                            "null"
                        ]
                    },
                    "weekly": {
                        "type": [
                            "integer",
                            "null"
                        ]
                    },
                    "monthly": {
                        "type": [
                            "integer",
                            "null"
                        ]
                    },
                    "unit": {
                        "type": "string",
                        "const": "minor"
                    }
                }
            },
            "Listing": {
                "type": "object",
                "properties": {
                    "id": {
                        "type": "string"
                    },
                    "title": {
                        "type": "string"
                    },
                    "type": {
                        "type": "string"
                    },
                    "city": {
                        "type": "string"
                    },
                    "country": {
                        "type": "string"
                    },
                    "address": {
                        "type": "string"
                    },
                    "rooms": {
                        "type": "integer"
                    },
                    "max_guests": {
                        "type": "integer"
                    },
                    "area_sqm": {
                        "type": [
                            "integer",
                            "null"
                        ]
                    },
                    "floor": {
                        "type": [
                            "integer",
                            "null"
                        ]
                    },
                    "amenities": {
                        "type": "array",
                        "items": {
                            "type": "string"
                        }
                    },
                    "photos": {
                        "type": "array",
                        "items": {
                            "type": "object",
                            "properties": {
                                "id": {
                                    "type": "string"
                                },
                                "url": {
                                    "type": "string"
                                }
                            }
                        }
                    },
                    "price": {
                        "$ref": "#/components/schemas/Money"
                    },
                    "min_stay_nights": {
                        "type": [
                            "integer",
                            "null"
                        ]
                    },
                    "description": {
                        "type": [
                            "string",
                            "null"
                        ]
                    },
                    "url": {
                        "type": "string"
                    }
                }
            },
            "Quote": {
                "type": "object",
                "properties": {
                    "available": {
                        "type": "boolean"
                    },
                    "reason": {
                        "type": [
                            "string",
                            "null"
                        ]
                    },
                    "listing_id": {
                        "type": "string"
                    },
                    "nights": {
                        "type": "integer"
                    },
                    "mode": {
                        "type": "string",
                        "enum": [
                            "nightly",
                            "weekly",
                            "monthly"
                        ]
                    },
                    "currency": {
                        "type": [
                            "string",
                            "null"
                        ]
                    },
                    "unit": {
                        "type": "string",
                        "const": "minor"
                    },
                    "rent_subtotal": {
                        "type": "integer"
                    },
                    "discount_percent": {
                        "type": "integer"
                    },
                    "discount_amount": {
                        "type": "integer"
                    },
                    "rent_total": {
                        "type": "integer"
                    },
                    "deposit": {
                        "type": "integer"
                    },
                    "lines": {
                        "type": "array",
                        "items": {
                            "type": "object",
                            "properties": {
                                "label": {
                                    "type": "string"
                                },
                                "amount": {
                                    "type": "integer"
                                }
                            }
                        }
                    }
                }
            },
            "Error": {
                "type": "object",
                "properties": {
                    "error": {
                        "type": "object",
                        "properties": {
                            "code": {
                                "type": "string"
                            },
                            "message": {
                                "type": "string"
                            }
                        }
                    }
                }
            }
        }
    },
    "paths": {
        "/listings": {
            "get": {
                "summary": "Search listings",
                "operationId": "searchListings",
                "parameters": [
                    {
                        "name": "city",
                        "in": "query",
                        "required": false,
                        "description": "City name.",
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "name": "check_in",
                        "in": "query",
                        "required": false,
                        "description": "Check-in date YYYY-MM-DD. With both dates, only available listings are returned.",
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "name": "check_out",
                        "in": "query",
                        "required": false,
                        "description": "Check-out date YYYY-MM-DD.",
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "name": "guests",
                        "in": "query",
                        "required": false,
                        "description": "Minimum guest capacity.",
                        "schema": {
                            "type": "integer"
                        }
                    },
                    {
                        "name": "sort",
                        "in": "query",
                        "required": false,
                        "description": "price_asc | price_desc | (default reputation-ranked).",
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "name": "page",
                        "in": "query",
                        "required": false,
                        "description": "Page number, 1-based.",
                        "schema": {
                            "type": "integer"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "A page of listings.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "data": {
                                            "type": "array",
                                            "items": {
                                                "$ref": "#/components/schemas/Listing"
                                            }
                                        },
                                        "meta": {
                                            "type": "object",
                                            "properties": {
                                                "total": {
                                                    "type": "integer"
                                                },
                                                "pages": {
                                                    "type": "integer"
                                                },
                                                "page": {
                                                    "type": "integer"
                                                },
                                                "per_page": {
                                                    "type": "integer"
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Missing or invalid token.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Credential lacks listings:read.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Rate limit exceeded (see Retry-After).",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/listings/{id}": {
            "get": {
                "summary": "Get a listing",
                "operationId": "getListing",
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "description": "Listing id.",
                        "schema": {
                            "type": "string"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "The listing.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "data": {
                                            "$ref": "#/components/schemas/Listing"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Listing not found or not listed.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/listings/{id}/quote": {
            "get": {
                "summary": "Price a stay",
                "operationId": "quoteStay",
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "description": "Listing id.",
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "name": "check_in",
                        "in": "query",
                        "required": true,
                        "description": "Check-in date YYYY-MM-DD.",
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "name": "check_out",
                        "in": "query",
                        "required": true,
                        "description": "Check-out date YYYY-MM-DD.",
                        "schema": {
                            "type": "string"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "A quote (available true/false).",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "data": {
                                            "$ref": "#/components/schemas/Quote"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "400": {
                        "description": "Invalid dates.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Listing not found.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/booking-requests": {
            "post": {
                "summary": "Propose a booking request",
                "operationId": "createBookingRequest",
                "description": "Scope bookings:write. NEVER books automatically: the guest confirms by email, then the owner approves. Body: listing_id, check_in, check_out, guests, guest_name, guest_email, message.",
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "required": [
                                    "listing_id",
                                    "check_in",
                                    "check_out",
                                    "guest_name",
                                    "guest_email"
                                ],
                                "properties": {
                                    "listing_id": {
                                        "type": "string"
                                    },
                                    "check_in": {
                                        "type": "string"
                                    },
                                    "check_out": {
                                        "type": "string"
                                    },
                                    "guests": {
                                        "type": "integer"
                                    },
                                    "guest_name": {
                                        "type": "string"
                                    },
                                    "guest_email": {
                                        "type": "string"
                                    },
                                    "message": {
                                        "type": "string"
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "A pending request awaiting the guest's email confirmation.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "data": {
                                            "type": "object",
                                            "properties": {
                                                "status": {
                                                    "type": "string",
                                                    "const": "pending_email_confirmation"
                                                },
                                                "request_id": {
                                                    "type": "string"
                                                },
                                                "expires_at": {
                                                    "type": "string"
                                                },
                                                "message": {
                                                    "type": "string"
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Credential lacks bookings:write.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "409": {
                        "description": "Dates unavailable.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Too many unconfirmed requests.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/booking-requests/{id}": {
            "get": {
                "summary": "Poll a booking request",
                "operationId": "bookingRequestStatus",
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "pending_email_confirmation / expired / activated (with the live booking_status).",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "data": {
                                            "type": "object"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Request not found.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/auth/otp/start": {
            "post": {
                "summary": "Request an email OTP",
                "operationId": "otpStart",
                "description": "Public. Emails a 6-digit code to the address so an agent with that inbox can obtain a token. Rate-limited.",
                "security": [],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "required": [
                                    "email"
                                ],
                                "properties": {
                                    "email": {
                                        "type": "string"
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Code sent (identical response whether or not the email exists)."
                    },
                    "429": {
                        "description": "Too many requests.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/auth/otp/verify": {
            "post": {
                "summary": "Exchange an OTP for a token",
                "operationId": "otpVerify",
                "description": "Public. Returns a personal access token (scopes listings:read, bookings:write) to use as Authorization: Bearer.",
                "security": [],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "required": [
                                    "email",
                                    "code"
                                ],
                                "properties": {
                                    "email": {
                                        "type": "string"
                                    },
                                    "code": {
                                        "type": "string"
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "A token.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "data": {
                                            "type": "object",
                                            "properties": {
                                                "token": {
                                                    "type": "string"
                                                },
                                                "token_type": {
                                                    "type": "string"
                                                },
                                                "scopes": {
                                                    "type": "array",
                                                    "items": {
                                                        "type": "string"
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "400": {
                        "description": "Invalid or expired code.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Too many attempts.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}