{
  "openapi": "3.1.0",
  "info": {
    "title": "Bonire Public API",
    "version": "1.0.0",
    "summary": "Read-only access to the aggregated event schedule for Israel.",
    "description": "Bonire aggregates public event listings from venues, organisers, ticketing systems and venue social accounts into one schedule, in English, Russian and Hebrew.\n\nThe /api/events, /api/venues and /api/artists endpoints are open and need no key. Prices and availability are snapshots of the source at the time it was last read; the ticket URL on a listing is authoritative. Bonire does not sell tickets.\n\nAgent guidance lives at /llms.txt. The full URL index is at /sitemap.xml. The home route also answers `Accept: text/markdown` with the current schedule as plain markdown.",
    "contact": {
      "name": "Bonire",
      "email": "hello@bonire.live",
      "url": "https://bonire.live/contact"
    }
  },
  "servers": [
    {
      "url": "https://bonire.live"
    }
  ],
  "tags": [
    {
      "name": "events",
      "description": "Event listings."
    },
    {
      "name": "venues",
      "description": "Places events happen."
    },
    {
      "name": "artists",
      "description": "Performers linked to events."
    },
    {
      "name": "search",
      "description": "Cross-entity search. Requires a key."
    }
  ],
  "paths": {
    "/api/events": {
      "get": {
        "operationId": "listEvents",
        "tags": [
          "events"
        ],
        "summary": "List events",
        "description": "Returns event listings with their venue, performers and genres. Use `upcoming=true` for events that have not happened yet, or bound the window with `dateFrom` and `dateTo`.",
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "description": "1-based page number. Defaults to 1.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Records per page. Values above 100 are clamped to 100.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            }
          },
          {
            "name": "upcoming",
            "in": "query",
            "description": "Set to `true` to return only events in the future.",
            "schema": {
              "type": "string",
              "enum": [
                "true",
                "false"
              ]
            }
          },
          {
            "name": "dateFrom",
            "in": "query",
            "description": "Earliest event date, ISO 8601 date or date-time.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "dateTo",
            "in": "query",
            "description": "Latest event date, ISO 8601 date or date-time.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "venue",
            "in": "query",
            "description": "Venue id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "artist",
            "in": "query",
            "description": "Artist id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "artistSlug",
            "in": "query",
            "description": "Artist slug.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "genre",
            "in": "query",
            "description": "Genre slug.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "sortBy",
            "in": "query",
            "description": "Field to sort on. Defaults to eventDate.",
            "schema": {
              "type": "string",
              "default": "eventDate"
            }
          },
          {
            "name": "sortOrder",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ],
              "default": "asc"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of events.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Event"
                      }
                    },
                    "pagination": {
                      "type": "object",
                      "description": "Page counters returned alongside every collection.",
                      "properties": {
                        "page": {
                          "type": "integer",
                          "minimum": 1
                        },
                        "limit": {
                          "type": "integer",
                          "minimum": 1,
                          "maximum": 100
                        },
                        "total": {
                          "type": "integer",
                          "description": "Total matching records."
                        },
                        "totalPages": {
                          "type": "integer"
                        }
                      },
                      "required": [
                        "page",
                        "limit",
                        "total",
                        "totalPages"
                      ]
                    }
                  },
                  "required": [
                    "data",
                    "pagination"
                  ]
                }
              }
            }
          },
          "500": {
            "description": "Error. The body carries a single human-readable message.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/api/venues": {
      "get": {
        "operationId": "listVenues",
        "tags": [
          "venues"
        ],
        "summary": "List venues",
        "description": "Returns venues with their city, address and a count of events attached to them.",
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "description": "1-based page number. Defaults to 1.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Records per page. Values above 100 are clamped to 100.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of venues.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Venue"
                      }
                    },
                    "pagination": {
                      "type": "object",
                      "description": "Page counters returned alongside every collection.",
                      "properties": {
                        "page": {
                          "type": "integer",
                          "minimum": 1
                        },
                        "limit": {
                          "type": "integer",
                          "minimum": 1,
                          "maximum": 100
                        },
                        "total": {
                          "type": "integer",
                          "description": "Total matching records."
                        },
                        "totalPages": {
                          "type": "integer"
                        }
                      },
                      "required": [
                        "page",
                        "limit",
                        "total",
                        "totalPages"
                      ]
                    }
                  },
                  "required": [
                    "data",
                    "pagination"
                  ]
                }
              }
            }
          },
          "500": {
            "description": "Error. The body carries a single human-readable message.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/api/artists": {
      "get": {
        "operationId": "listArtists",
        "tags": [
          "artists"
        ],
        "summary": "List artists",
        "description": "Returns performers known to Bonire, optionally filtered by name or genre.",
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "description": "1-based page number. Defaults to 1.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Records per page. Values above 100 are clamped to 100.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            }
          },
          {
            "name": "search",
            "in": "query",
            "description": "Case-insensitive name match.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "genre",
            "in": "query",
            "description": "Genre name or slug.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of artists.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Artist"
                      }
                    },
                    "pagination": {
                      "type": "object",
                      "description": "Page counters returned alongside every collection.",
                      "properties": {
                        "page": {
                          "type": "integer",
                          "minimum": 1
                        },
                        "limit": {
                          "type": "integer",
                          "minimum": 1,
                          "maximum": 100
                        },
                        "total": {
                          "type": "integer",
                          "description": "Total matching records."
                        },
                        "totalPages": {
                          "type": "integer"
                        }
                      },
                      "required": [
                        "page",
                        "limit",
                        "total",
                        "totalPages"
                      ]
                    }
                  },
                  "required": [
                    "data",
                    "pagination"
                  ]
                }
              }
            }
          },
          "500": {
            "description": "Error. The body carries a single human-readable message.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/api/public/v1/search": {
      "get": {
        "operationId": "searchBonire",
        "tags": [
          "search"
        ],
        "summary": "Search across events, venues and artists",
        "description": "Requires an API key with the `search.read` scope; unauthenticated requests answer 401. The date window is bounded — an over-long `from`/`to` range answers 422.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "description": "Query text.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "from",
            "in": "query",
            "description": "Window start, ISO 8601 date.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "to",
            "in": "query",
            "description": "Window end, ISO 8601 date.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Search results."
          },
          "400": {
            "description": "Error. The body carries a single human-readable message.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Error. The body carries a single human-readable message.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "422": {
            "description": "Error. The body carries a single human-readable message.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "apiKey": {
        "type": "apiKey",
        "in": "header",
        "name": "Authorization",
        "description": "API key with the `search.read` scope."
      }
    },
    "schemas": {
      "LocalizedText": {
        "type": "object",
        "description": "Per-language values keyed by locale.",
        "properties": {
          "en": {
            "type": "string"
          },
          "ru": {
            "type": "string"
          },
          "he": {
            "type": "string"
          }
        },
        "additionalProperties": {
          "type": "string"
        }
      },
      "Genre": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "slug": {
            "type": "string"
          },
          "nameEn": {
            "type": "string"
          },
          "nameRu": {
            "type": "string"
          },
          "nameHe": {
            "type": "string"
          }
        }
      },
      "EventArtist": {
        "type": "object",
        "description": "A performer as attached to one event.",
        "properties": {
          "id": {
            "type": [
              "string",
              "null"
            ]
          },
          "slug": {
            "type": [
              "string",
              "null"
            ]
          },
          "name": {
            "type": "string"
          },
          "genre": {
            "type": [
              "string",
              "null"
            ]
          },
          "profileId": {
            "type": [
              "string",
              "null"
            ]
          },
          "profileSlug": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "additionalProperties": true
      },
      "Event": {
        "type": "object",
        "description": "One listing. Times are wall-clock in `eventTimezone`, usually Asia/Jerusalem.",
        "properties": {
          "title": {
            "type": "string"
          },
          "eventDate": {
            "type": "string",
            "format": "date-time"
          },
          "eventTime": {
            "type": [
              "string",
              "null"
            ],
            "description": "Local start time, HH:MM."
          },
          "eventTimezone": {
            "type": [
              "string",
              "null"
            ]
          },
          "eventLanguage": {
            "type": [
              "string",
              "null"
            ]
          },
          "originalLanguage": {
            "type": [
              "string",
              "null"
            ]
          },
          "dubbedLanguage": {
            "type": [
              "string",
              "null"
            ]
          },
          "isDubbed": {
            "type": [
              "boolean",
              "null"
            ]
          },
          "subtitleLanguages": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "string"
            }
          },
          "auditorium": {
            "type": [
              "string",
              "null"
            ]
          },
          "price": {
            "type": [
              "string",
              "null"
            ],
            "description": "Price as published by the source."
          },
          "venue": {
            "$ref": "#/components/schemas/Venue"
          },
          "artists": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EventArtist"
            }
          },
          "genres": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Genre"
            }
          },
          "sourcePostUrl": {
            "type": [
              "string",
              "null"
            ],
            "description": "Where the listing was read from."
          },
          "imageUrls": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "string"
            }
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "additionalProperties": true
      },
      "Venue": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "nameI18n": {
            "$ref": "#/components/schemas/LocalizedText"
          },
          "slug": {
            "type": "string"
          },
          "category": {
            "type": [
              "string",
              "null"
            ]
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "city": {
            "type": [
              "string",
              "null"
            ]
          },
          "citySlug": {
            "type": [
              "string",
              "null"
            ]
          },
          "address": {
            "type": [
              "string",
              "null"
            ]
          },
          "addressI18n": {
            "$ref": "#/components/schemas/LocalizedText"
          },
          "website": {
            "type": [
              "string",
              "null"
            ]
          },
          "eventCount": {
            "type": "integer",
            "description": "Events attached to this venue."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "additionalProperties": true
      },
      "Artist": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "slug": {
            "type": [
              "string",
              "null"
            ]
          },
          "name": {
            "type": "string"
          },
          "genre": {
            "type": [
              "string",
              "null"
            ]
          },
          "canonicalSource": {
            "type": [
              "string",
              "null"
            ]
          },
          "profileId": {
            "type": [
              "string",
              "null"
            ]
          },
          "profileSlug": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "additionalProperties": true
      }
    }
  }
}