{
  "openapi": "3.0.3",
  "info": {
    "title": "Parkeerplaats.nl Public API",
    "version": "1.0.0",
    "description": "API voor het beheren van parkeerplaats-advertenties op Parkeerplaats.nl. Bedoeld voor vastgoedsoftware, CRM-systemen en makelaars die hun aanbod automatisch willen synchroniseren.",
    "contact": {
      "name": "Parkeerplaats.nl",
      "email": "parkeerplaatsnl@gmail.com",
      "url": "https://www.parkeerplaats.nl/contact"
    }
  },
  "servers": [
    {
      "url": "https://www.parkeerplaats.nl",
      "description": "Production"
    }
  ],
  "security": [
    {
      "ApiKeyAuth": []
    }
  ],
  "paths": {
    "/api/v1/listings": {
      "get": {
        "summary": "Lijst uw advertenties",
        "description": "Geeft een gepagineerde lijst van uw eigen advertenties terug.",
        "operationId": "listListings",
        "tags": ["Listings"],
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "schema": { "type": "integer", "default": 50, "maximum": 100 },
            "description": "Aantal resultaten per pagina (max 100)"
          },
          {
            "name": "offset",
            "in": "query",
            "schema": { "type": "integer", "default": 0 },
            "description": "Offset voor paginering"
          },
          {
            "name": "status",
            "in": "query",
            "schema": { "$ref": "#/components/schemas/ListingStatus" },
            "description": "Filter op status"
          }
        ],
        "responses": {
          "200": {
            "description": "Lijst van advertenties",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": { "$ref": "#/components/schemas/Listing" }
                    },
                    "pagination": { "$ref": "#/components/schemas/Pagination" }
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      },
      "post": {
        "summary": "Maak een nieuwe advertentie",
        "description": "Maakt een nieuwe parkeerplaats-advertentie aan. Als `external_id` is opgegeven en al bestaat voor uw account, wordt de bestaande advertentie bijgewerkt (upsert).",
        "operationId": "createListing",
        "tags": ["Listings"],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/ListingInput" },
              "example": {
                "title": "Garagebox centrum Amsterdam",
                "description": "Ruime garagebox nabij het Vondelpark. Direct beschikbaar.",
                "type": "garagebox",
                "transaction_type": "huur",
                "price": 150,
                "price_suffix": "p/mnd",
                "address": "Keizersgracht 100",
                "postal_code": "1015AA",
                "city": "Amsterdam",
                "neighborhood": "Centrum",
                "lat": 52.3676,
                "lng": 4.9041,
                "features": { "verlichting": true, "laadpaal": false },
                "status": "active",
                "external_id": "CRM-12345"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Advertentie aangemaakt",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": { "$ref": "#/components/schemas/Listing" }
                  }
                }
              }
            }
          },
          "200": {
            "description": "Advertentie bijgewerkt via upsert (external_id bestond al)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": { "$ref": "#/components/schemas/Listing" },
                    "upserted": { "type": "boolean", "example": true }
                  }
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/api/v1/listings/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": { "type": "string", "format": "uuid" },
          "description": "Advertentie UUID"
        }
      ],
      "get": {
        "summary": "Haal een advertentie op",
        "operationId": "getListing",
        "tags": ["Listings"],
        "responses": {
          "200": {
            "description": "Advertentie details",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": { "$ref": "#/components/schemas/Listing" }
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      },
      "patch": {
        "summary": "Werk een advertentie bij",
        "description": "Werk een of meerdere velden van een advertentie bij. Alleen meegegeven velden worden gewijzigd.",
        "operationId": "updateListing",
        "tags": ["Listings"],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/ListingInput" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Advertentie bijgewerkt",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": { "$ref": "#/components/schemas/Listing" }
                  }
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      },
      "delete": {
        "summary": "Verwijder een advertentie",
        "operationId": "deleteListing",
        "tags": ["Listings"],
        "responses": {
          "200": {
            "description": "Advertentie verwijderd",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "deleted": { "type": "boolean", "example": true }
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/api/v1/listings/{id}/images": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": { "type": "string", "format": "uuid" },
          "description": "Advertentie UUID"
        }
      ],
      "post": {
        "summary": "Upload een afbeelding",
        "description": "Upload een afbeelding voor een advertentie. Stuur als multipart/form-data met veld `file`. Max 10MB.",
        "operationId": "uploadImage",
        "tags": ["Images"],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": ["file"],
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary",
                    "description": "Afbeelding (JPEG, PNG, WebP). Max 10MB."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Afbeelding geüpload",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "url": { "type": "string", "format": "uri" },
                    "images": {
                      "type": "array",
                      "items": { "type": "string", "format": "uri" }
                    }
                  }
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      },
      "delete": {
        "summary": "Verwijder een afbeelding",
        "description": "Verwijder een afbeelding van een advertentie. Geef de volledige URL mee in de request body.",
        "operationId": "deleteImage",
        "tags": ["Images"],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["url"],
                "properties": {
                  "url": {
                    "type": "string",
                    "format": "uri",
                    "description": "De volledige URL van de afbeelding"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Afbeelding verwijderd",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "images": {
                      "type": "array",
                      "items": { "type": "string", "format": "uri" }
                    }
                  }
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "ApiKeyAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "pk_live_...",
        "description": "API-sleutel aangemaakt in uw dashboard op /dashboard/api-keys. Stuur als Bearer token."
      }
    },
    "schemas": {
      "ListingPropertyType": {
        "type": "string",
        "enum": ["parkeerplaats", "garagebox", "parkeerkelder", "oprit"],
        "description": "Type parkeerplaats"
      },
      "TransactionType": {
        "type": "string",
        "enum": ["koop", "huur"],
        "description": "Koop of huur"
      },
      "ListingStatus": {
        "type": "string",
        "enum": ["draft", "active", "sold", "verhuurd", "verkocht_onder_voorbehoud", "ingetrokken", "expired"],
        "description": "Status van de advertentie"
      },
      "Listing": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "title": { "type": "string" },
          "description": { "type": "string", "nullable": true },
          "type": { "$ref": "#/components/schemas/ListingPropertyType" },
          "transaction_type": { "$ref": "#/components/schemas/TransactionType" },
          "price": { "type": "number", "nullable": true },
          "price_suffix": { "type": "string", "nullable": true, "example": "p/mnd" },
          "address": { "type": "string", "nullable": true },
          "city": { "type": "string", "nullable": true },
          "neighborhood": { "type": "string", "nullable": true },
          "postal_code": { "type": "string", "nullable": true },
          "lat": { "type": "number", "nullable": true },
          "lng": { "type": "number", "nullable": true },
          "features": { "type": "object", "additionalProperties": true },
          "images": { "type": "array", "items": { "type": "string", "format": "uri" } },
          "status": { "$ref": "#/components/schemas/ListingStatus" },
          "external_id": { "type": "string", "nullable": true },
          "is_featured": { "type": "boolean" },
          "created_at": { "type": "string", "format": "date-time" },
          "updated_at": { "type": "string", "format": "date-time" }
        }
      },
      "ListingInput": {
        "type": "object",
        "required": ["title", "transaction_type"],
        "properties": {
          "title": { "type": "string", "description": "Titel van de advertentie" },
          "description": { "type": "string", "nullable": true },
          "type": { "$ref": "#/components/schemas/ListingPropertyType" },
          "transaction_type": { "$ref": "#/components/schemas/TransactionType" },
          "price": { "type": "number", "nullable": true, "description": "Prijs in euro's" },
          "price_suffix": { "type": "string", "nullable": true, "description": "bijv. 'p/mnd', 'k.k.', 'v.o.n.'" },
          "address": { "type": "string", "nullable": true },
          "city": { "type": "string", "nullable": true },
          "neighborhood": { "type": "string", "nullable": true },
          "postal_code": { "type": "string", "nullable": true },
          "lat": { "type": "number", "nullable": true, "description": "Breedtegraad" },
          "lng": { "type": "number", "nullable": true, "description": "Lengtegraad" },
          "features": { "type": "object", "additionalProperties": true, "description": "Kenmerken als key-value pairs" },
          "status": { "$ref": "#/components/schemas/ListingStatus" },
          "external_id": { "type": "string", "nullable": true, "description": "Uw eigen ID voor synchronisatie. Uniek per account." }
        }
      },
      "Pagination": {
        "type": "object",
        "properties": {
          "total": { "type": "integer" },
          "limit": { "type": "integer" },
          "offset": { "type": "integer" }
        }
      },
      "Error": {
        "type": "object",
        "properties": {
          "error": { "type": "string" }
        }
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "Ongeldige of ontbrekende API-sleutel",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" },
            "example": { "error": "Invalid API key" }
          }
        }
      },
      "BadRequest": {
        "description": "Ongeldige invoer",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" },
            "example": { "error": "title is required" }
          }
        }
      },
      "NotFound": {
        "description": "Resource niet gevonden",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" },
            "example": { "error": "Listing not found" }
          }
        }
      },
      "RateLimited": {
        "description": "Rate limit overschreden (max 100 requests/minuut)",
        "headers": {
          "Retry-After": {
            "schema": { "type": "integer" },
            "description": "Seconden tot rate limit reset"
          },
          "X-RateLimit-Limit": {
            "schema": { "type": "integer" },
            "description": "Max verzoeken per minuut"
          }
        },
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" },
            "example": { "error": "Rate limit exceeded. Max 100 requests per minute." }
          }
        }
      }
    }
  }
}
