{
  "openapi": "3.1.0",
  "info": {
    "title": "Chef Joice Public API",
    "version": "1.0.0",
    "summary": "Read-only API with Chef Joice's menu, services, delivery zones and business details.",
    "description": "Public, unauthenticated, read-only JSON API of Chef Joice (Brazilian personal chef and meal-prep delivery in Barcelona). Resources are selected with the `resource` query parameter. Ordering is not available through the API; send users to https://chefjoice.com/menu or WhatsApp +34 613 963 067. Errors use RFC 9457 problem details with `code` and `hint`. Limit: 60 requests per minute per client, counted by each server instance; follow the RateLimit headers and Retry-After on 429. Versioning: fields are not removed or renamed within a major version; breaking changes are announced at https://chefjoice.com/developers#versioning at least 90 days ahead, with Deprecation (RFC 9745) and Sunset (RFC 8594) headers on affected responses.",
    "contact": {
      "name": "Chef Joice",
      "email": "info@chefjoice.com",
      "url": "https://chefjoice.com/developers"
    }
  },
  "servers": [
    {
      "url": "https://chefjoice.com"
    }
  ],
  "security": [],
  "externalDocs": {
    "description": "Developer and agent documentation",
    "url": "https://chefjoice.com/developers"
  },
  "paths": {
    "/functions/api": {
      "get": {
        "operationId": "getResource",
        "summary": "Get a resource",
        "description": "Without `resource`, returns the API index. `resource=menu` accepts the optional filters `category`, `origin` and `dietary`.",
        "parameters": [
          {
            "name": "resource",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "business",
                "services",
                "menu",
                "delivery-zones",
                "faq",
                "openapi",
                "health"
              ]
            },
            "description": "Which resource to return. Omit for the index."
          },
          {
            "name": "category",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "Brazilian_Salgados",
                "Spanish_Classics",
                "Ready_Meals",
                "Catering"
              ]
            },
            "description": "Menu only: filter by category."
          },
          {
            "name": "origin",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "Brazil",
                "Spain",
                "Fusion"
              ]
            },
            "description": "Menu only: filter by cuisine origin."
          },
          {
            "name": "dietary",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "Sin Gluten",
                "Vegetariano",
                "Bajo en Carbs",
                "Sobremesas"
              ]
            },
            "description": "Menu only: keep items with this dietary tag."
          }
        ],
        "security": [],
        "responses": {
          "200": {
            "description": "The requested resource.",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Invalid parameter value (code: invalid_parameter).",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "Unknown resource (code: resource_not_found).",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "405": {
            "description": "Method not allowed; the API is read-only (code: method_not_allowed).",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; wait Retry-After seconds (code: rate_limited).",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected error (code: internal_error).",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "502": {
            "description": "Menu database unavailable (code: upstream_unavailable).",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Problem": {
        "type": "object",
        "description": "RFC 9457 problem details with Chef Joice extension members.",
        "required": [
          "type",
          "title",
          "status",
          "detail",
          "code",
          "hint"
        ],
        "properties": {
          "type": {
            "type": "string",
            "format": "uri"
          },
          "title": {
            "type": "string"
          },
          "status": {
            "type": "integer"
          },
          "detail": {
            "type": "string"
          },
          "instance": {
            "type": "string"
          },
          "code": {
            "type": "string",
            "enum": [
              "invalid_parameter",
              "resource_not_found",
              "method_not_allowed",
              "rate_limited",
              "upstream_unavailable",
              "internal_error"
            ]
          },
          "hint": {
            "type": "string"
          },
          "docs": {
            "type": "string",
            "format": "uri"
          }
        }
      }
    },
    "headers": {
      "RateLimit-Policy": {
        "description": "Quota policy, e.g. `\"default\";q=60;w=60`.",
        "schema": {
          "type": "string"
        }
      },
      "RateLimit": {
        "description": "Remaining quota and seconds until reset, e.g. `\"default\";r=59;t=60`.",
        "schema": {
          "type": "string"
        }
      },
      "RateLimit-Limit": {
        "description": "Requests allowed per window.",
        "schema": {
          "type": "integer"
        }
      },
      "RateLimit-Remaining": {
        "description": "Requests left in the current window.",
        "schema": {
          "type": "integer"
        }
      },
      "RateLimit-Reset": {
        "description": "Seconds until the window resets.",
        "schema": {
          "type": "integer"
        }
      },
      "Retry-After": {
        "description": "Seconds to wait before retrying (429 only).",
        "schema": {
          "type": "integer"
        }
      }
    },
    "responses": {
      "Problem": {
        "description": "Error described as RFC 9457 problem details.",
        "headers": {
          "RateLimit-Policy": {
            "$ref": "#/components/headers/RateLimit-Policy"
          },
          "RateLimit": {
            "$ref": "#/components/headers/RateLimit"
          },
          "RateLimit-Limit": {
            "$ref": "#/components/headers/RateLimit-Limit"
          },
          "RateLimit-Remaining": {
            "$ref": "#/components/headers/RateLimit-Remaining"
          },
          "RateLimit-Reset": {
            "$ref": "#/components/headers/RateLimit-Reset"
          }
        },
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "RateLimited": {
        "description": "Too many requests. Wait `Retry-After` seconds.",
        "headers": {
          "RateLimit-Policy": {
            "$ref": "#/components/headers/RateLimit-Policy"
          },
          "RateLimit": {
            "$ref": "#/components/headers/RateLimit"
          },
          "RateLimit-Limit": {
            "$ref": "#/components/headers/RateLimit-Limit"
          },
          "RateLimit-Remaining": {
            "$ref": "#/components/headers/RateLimit-Remaining"
          },
          "RateLimit-Reset": {
            "$ref": "#/components/headers/RateLimit-Reset"
          },
          "Retry-After": {
            "$ref": "#/components/headers/Retry-After"
          }
        },
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      }
    }
  }
}
