{
  "openapi": "3.1.0",
  "info": {
    "title": "Filipe Santos Fotografia",
    "version": "0.4.0",
    "description": "Porto photo studio: prices, free studio times, bookings with MB WAY/Multibanco/card payment, gift vouchers, quotes, and the AI caricature booth software for event vendors. No authentication; personal data only when the user books.",
    "contact": {
      "email": "geral@filipesantosfotografia.com",
      "url": "https://filipesantosfotografia.com"
    },
    "termsOfService": "https://mcp.filipesantosfotografia.com/termos"
  },
  "servers": [
    {
      "url": "https://mcp.filipesantosfotografia.com"
    }
  ],
  "paths": {
    "/api/tools/list_services": {
      "post": {
        "operationId": "list_services",
        "summary": "Serviços e preços",
        "description": "Returns Filipe Santos Fotografia's public catalog with prices: studio portrait and family packages, studio rental, add-ons, corporate photo/video and extras, wedding collections and price range, baptism packages, photobooth / 360 videobooth / AI caricature mirror packages, studio events, other sessions, the AI caricature mirror software licensed to event vendors, delivery times and contact. Use when the user asks what the studio offers, how much something costs, or how booking works. Optionally filter by category.",
        "x-openai-isConsequential": false,
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "category": {
                    "default": "all",
                    "description": "Which part of the catalog to return: studio = bookable portraits, family sessions and studio rental; software = AI caricature mirror software for event vendors",
                    "type": "string",
                    "enum": [
                      "all",
                      "studio",
                      "corporate",
                      "wedding",
                      "baptism",
                      "photobooth",
                      "studio_events",
                      "other",
                      "software"
                    ]
                  }
                },
                "$schema": "http://json-schema.org/draft-07/schema#"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Result",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "422": {
            "description": "The request was understood but refused (e.g. time no longer free); `error` says why"
          }
        }
      }
    },
    "/api/tools/get_studio_slots": {
      "post": {
        "operationId": "get_studio_slots",
        "summary": "Horários livres no estúdio",
        "description": "Lists free start times at Estúdio 266 for a bookable package between two dates (max 21 days). Monday–Friday, 10:00–18:00 Lisbon time, closed on Portuguese public holidays, booked at least 24 h ahead. Times are ISO 8601 with the Lisbon offset.",
        "x-openai-isConsequential": false,
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "package": {
                    "type": "string",
                    "enum": [
                      "base",
                      "completo",
                      "equipa",
                      "familia",
                      "natal",
                      "aluguer_2h",
                      "aluguer_4h",
                      "aluguer_8h"
                    ],
                    "description": "base (35€ portrait, 1 background, 2 photos) · completo (150€, 2–3 sets, 10 photos) · equipa (250€, up to 4 people, 20 photos) · familia (200€ family, pregnancy or pets, up to 6 people + 2 pets, 15 photos) · natal (159€ Christmas session, 10 photos, bookable 1 Nov–23 Dec) · aluguer_2h / aluguer_4h / aluguer_8h (studio rental without photographer: 110.70€ / 196.80€ / 344.40€ VAT incl.)"
                  },
                  "from_date": {
                    "type": "string",
                    "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
                    "description": "First day to check, YYYY-MM-DD"
                  },
                  "to_date": {
                    "type": "string",
                    "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
                    "description": "Last day to check, YYYY-MM-DD"
                  }
                },
                "required": [
                  "package",
                  "from_date",
                  "to_date"
                ],
                "$schema": "http://json-schema.org/draft-07/schema#"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Result",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "422": {
            "description": "The request was understood but refused (e.g. time no longer free); `error` says why"
          }
        }
      }
    },
    "/api/tools/request_studio_booking": {
      "post": {
        "operationId": "request_studio_booking",
        "summary": "Pré-reservar sessão de estúdio",
        "description": "Pre-books a studio portrait, family session or studio rental at a free slot from get_studio_slots and starts payment. Only call after the user has explicitly confirmed the package, the exact start time and their contact details. Suggest MB WAY first (instant); a Multibanco reference is only possible for sessions more than 48 h away. A gift voucher or studio credit code (FSF-XXXX-XXXX) pays all or part of the price. The slot is held for a limited time (hold_expires_at) and the booking is confirmed once paid. Offer extra edited photos (5€ each) and make-up when relevant. Calling again with the same email, package and time returns the same pre-booking.",
        "x-openai-isConsequential": true,
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "package": {
                    "type": "string",
                    "enum": [
                      "base",
                      "completo",
                      "equipa",
                      "familia",
                      "natal",
                      "aluguer_2h",
                      "aluguer_4h",
                      "aluguer_8h"
                    ],
                    "description": "base (35€ portrait, 1 background, 2 photos) · completo (150€, 2–3 sets, 10 photos) · equipa (250€, up to 4 people, 20 photos) · familia (200€ family, pregnancy or pets, up to 6 people + 2 pets, 15 photos) · natal (159€ Christmas session, 10 photos, bookable 1 Nov–23 Dec) · aluguer_2h / aluguer_4h / aluguer_8h (studio rental without photographer: 110.70€ / 196.80€ / 344.40€ VAT incl.)"
                  },
                  "start": {
                    "type": "string",
                    "description": "Exact start time as returned by get_studio_slots (ISO 8601 with offset)"
                  },
                  "name": {
                    "type": "string",
                    "minLength": 2,
                    "maxLength": 120,
                    "description": "Client's full name"
                  },
                  "email": {
                    "type": "string",
                    "format": "email",
                    "pattern": "^(?:[A-Za-z0-9_'+\\-]+\\.)*[A-Za-z0-9_'+\\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$",
                    "description": "Client's email for confirmation and payment instructions"
                  },
                  "phone": {
                    "description": "Mobile number; required for MB WAY",
                    "type": "string",
                    "pattern": "^\\+?[0-9 ]{9,16}$"
                  },
                  "payment_method": {
                    "type": "string",
                    "enum": [
                      "MBWAY",
                      "MB",
                      "CCARD"
                    ],
                    "description": "MB WAY, Multibanco reference or card"
                  },
                  "extras": {
                    "default": [],
                    "description": "Optional add-ons: exterior (+75€, outdoor session), maquilhagem (+120€), cabelo_maquilhagem (+200€); assistente_iluminacao (+61.50€) only for studio rental",
                    "maxItems": 2,
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "exterior",
                        "maquilhagem",
                        "cabelo_maquilhagem",
                        "assistente_iluminacao"
                      ]
                    }
                  },
                  "notes": {
                    "description": "What the photos are for (e.g. LinkedIn, website); optional",
                    "type": "string",
                    "maxLength": 500
                  },
                  "extra_photos": {
                    "default": 0,
                    "description": "Extra edited photos on top of the package, 5€ each",
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 30
                  },
                  "voucher_code": {
                    "description": "Gift voucher or studio credit code, if the client has one",
                    "type": "string",
                    "pattern": "^FSF-[A-Z0-9]{4}-[A-Z0-9]{4}$"
                  },
                  "nif": {
                    "description": "Tax number (NIF) for the invoice, e.g. for companies; optional",
                    "type": "string",
                    "pattern": "^[0-9A-Za-z]{9,14}$"
                  }
                },
                "required": [
                  "package",
                  "start",
                  "name",
                  "email",
                  "payment_method"
                ],
                "$schema": "http://json-schema.org/draft-07/schema#"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Result",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "422": {
            "description": "The request was understood but refused (e.g. time no longer free); `error` says why"
          }
        }
      }
    },
    "/api/tools/cancel_studio_booking": {
      "post": {
        "operationId": "cancel_studio_booking",
        "summary": "Cancelar sessão de estúdio",
        "description": "Cancels a studio booking identified by its reference (e.g. FSF-1A2B3C4D) and the email used to book. Before calling, tell the user the policy and get explicit confirmation: there are no cash refunds; cancelling at least 24 h before turns the amount paid into studio credit (a code valid 12 months for any session); less than 24 h before is not possible. Offer to reschedule instead (up to 2 times). Unpaid pre-bookings are simply released; amounts paid with a voucher go back to the voucher.",
        "x-openai-isConsequential": true,
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "booking_ref": {
                    "type": "string",
                    "pattern": "^FSF-[A-Z0-9]{6,12}$",
                    "description": "Booking reference, e.g. FSF-1A2B3C4D"
                  },
                  "email": {
                    "type": "string",
                    "format": "email",
                    "pattern": "^(?:[A-Za-z0-9_'+\\-]+\\.)*[A-Za-z0-9_'+\\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$",
                    "description": "Email used when booking"
                  }
                },
                "required": [
                  "booking_ref",
                  "email"
                ],
                "$schema": "http://json-schema.org/draft-07/schema#"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Result",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "422": {
            "description": "The request was understood but refused (e.g. time no longer free); `error` says why"
          }
        }
      }
    },
    "/api/tools/reschedule_studio_booking": {
      "post": {
        "operationId": "reschedule_studio_booking",
        "summary": "Remarcar sessão de estúdio",
        "description": "Moves an existing studio booking (up to 2 times) to a new free time, identified by booking reference and the email used to book. Check free times first with get_studio_slots for the same package and confirm the new time with the user before calling. Not possible less than 24 hours before the current session.",
        "x-openai-isConsequential": true,
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "booking_ref": {
                    "type": "string",
                    "pattern": "^FSF-[A-Z0-9]{6,12}$"
                  },
                  "email": {
                    "type": "string",
                    "format": "email",
                    "pattern": "^(?:[A-Za-z0-9_'+\\-]+\\.)*[A-Za-z0-9_'+\\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"
                  },
                  "new_start": {
                    "type": "string",
                    "description": "New start time as returned by get_studio_slots (ISO 8601 with offset)"
                  }
                },
                "required": [
                  "booking_ref",
                  "email",
                  "new_start"
                ],
                "$schema": "http://json-schema.org/draft-07/schema#"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Result",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "422": {
            "description": "The request was understood but refused (e.g. time no longer free); `error` says why"
          }
        }
      }
    },
    "/api/tools/check_event_date": {
      "post": {
        "operationId": "check_event_date",
        "summary": "Ver se a data está livre",
        "description": "Checks whether the studio can still take an event on a given date (weddings, baptisms, corporate events and other on-location work; the studio has two teams). Returns available, limited (one team left) or fully_booked. The answer is indicative; the date is only reserved after the proposal is accepted. Use before request_quote when the user has a date.",
        "x-openai-isConsequential": false,
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "date": {
                    "type": "string",
                    "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
                    "description": "Event date, YYYY-MM-DD"
                  },
                  "service": {
                    "default": "casamento",
                    "description": "casamento = Casamento (foto e/ou vídeo); batizado = Batizado ou Profissão de Fé; corporativo = Evento corporativo, vídeo institucional, produto, hotelaria ou arquitetura; empresa_ambientes = Sessão de ambientes / quotidiano na empresa; retratos_empresa = Retratos da equipa na empresa (deslocação); photobooth = Photobooth, 360 Videobooth ou Espelho Caricaturista com IA; sessao_casal = Sessão de casal, noivado ou solteiros; pedido_casamento = Pedido de casamento surpresa; sessao_exterior = Sessão de família ou grávida em exterior; sessao_natal = Sessão de Natal em estúdio; sessao_tematica = Sessão temática ou criativa em estúdio; evento_estudio = Evento no Estúdio 266 (aniversário, chá de bebé, mini-casamento, workshop); impressoes = Impressões Fine Art, álbuns ou e-books; outro = Outro pedido",
                    "type": "string",
                    "enum": [
                      "casamento",
                      "batizado",
                      "corporativo",
                      "empresa_ambientes",
                      "retratos_empresa",
                      "photobooth",
                      "sessao_casal",
                      "pedido_casamento",
                      "sessao_exterior",
                      "sessao_natal",
                      "sessao_tematica",
                      "evento_estudio",
                      "impressoes",
                      "outro"
                    ]
                  }
                },
                "required": [
                  "date"
                ],
                "$schema": "http://json-schema.org/draft-07/schema#"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Result",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "422": {
            "description": "The request was understood but refused (e.g. time no longer free); `error` says why"
          }
        }
      }
    },
    "/api/tools/request_quote": {
      "post": {
        "operationId": "request_quote",
        "summary": "Pedir orçamento",
        "description": "Sends a quote request to the studio for anything not bookable directly (weddings, baptisms, corporate photo/video, photobooth / 360 videobooth / AI caricature mirror, couple or proposal sessions, studio events, prints). Creates the request in the studio's system; the client gets a confirmation email and a personal proposal within 24 hours. Only call after the user confirms the details and agrees to be contacted.",
        "x-openai-isConsequential": true,
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "service": {
                    "type": "string",
                    "enum": [
                      "casamento",
                      "batizado",
                      "corporativo",
                      "empresa_ambientes",
                      "retratos_empresa",
                      "photobooth",
                      "sessao_casal",
                      "pedido_casamento",
                      "sessao_exterior",
                      "sessao_natal",
                      "sessao_tematica",
                      "evento_estudio",
                      "impressoes",
                      "outro"
                    ],
                    "description": "casamento = Casamento (foto e/ou vídeo); batizado = Batizado ou Profissão de Fé; corporativo = Evento corporativo, vídeo institucional, produto, hotelaria ou arquitetura; empresa_ambientes = Sessão de ambientes / quotidiano na empresa; retratos_empresa = Retratos da equipa na empresa (deslocação); photobooth = Photobooth, 360 Videobooth ou Espelho Caricaturista com IA; sessao_casal = Sessão de casal, noivado ou solteiros; pedido_casamento = Pedido de casamento surpresa; sessao_exterior = Sessão de família ou grávida em exterior; sessao_natal = Sessão de Natal em estúdio; sessao_tematica = Sessão temática ou criativa em estúdio; evento_estudio = Evento no Estúdio 266 (aniversário, chá de bebé, mini-casamento, workshop); impressoes = Impressões Fine Art, álbuns ou e-books; outro = Outro pedido"
                  },
                  "name": {
                    "type": "string",
                    "minLength": 2,
                    "maxLength": 120,
                    "description": "Client's name (for weddings, both names if given)"
                  },
                  "email": {
                    "type": "string",
                    "format": "email",
                    "pattern": "^(?:[A-Za-z0-9_'+\\-]+\\.)*[A-Za-z0-9_'+\\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"
                  },
                  "phone": {
                    "description": "Mobile / WhatsApp, optional",
                    "type": "string",
                    "pattern": "^\\+?[0-9 ]{9,16}$"
                  },
                  "event_date": {
                    "description": "Event date if known, YYYY-MM-DD",
                    "type": "string",
                    "pattern": "^\\d{4}-\\d{2}-\\d{2}$"
                  },
                  "location": {
                    "description": "Venue, city or 'Estúdio 266'",
                    "type": "string",
                    "maxLength": 200
                  },
                  "guests": {
                    "description": "Number of guests or people, if relevant",
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 5000
                  },
                  "budget": {
                    "description": "Budget range if the user mentioned one",
                    "type": "string",
                    "maxLength": 80
                  },
                  "details": {
                    "type": "string",
                    "minLength": 5,
                    "maxLength": 1500,
                    "description": "What they need: coverage, hours, photo and/or video, add-ons, questions"
                  },
                  "language": {
                    "default": "pt",
                    "description": "Language for the reply",
                    "type": "string",
                    "enum": [
                      "pt",
                      "en",
                      "es",
                      "fr"
                    ]
                  }
                },
                "required": [
                  "service",
                  "name",
                  "email",
                  "details"
                ],
                "$schema": "http://json-schema.org/draft-07/schema#"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Result",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "422": {
            "description": "The request was understood but refused (e.g. time no longer free); `error` says why"
          }
        }
      }
    },
    "/api/tools/buy_gift_voucher": {
      "post": {
        "operationId": "buy_gift_voucher",
        "summary": "Comprar vale-oferta",
        "description": "Sells a digital gift voucher for the studio (great for Christmas, birthdays, Mother's/Father's Day): either a specific session (base 35€, completo 150€, equipa 250€, familia 200€, natal 159€) or an amount (50, 100, 150, 200 or 250€). Paid now by MB WAY, Multibanco or card; after payment the buyer receives the voucher by email, ready to print or forward, with a code valid 12 months for any studio session (unused balance stays on the voucher). Only call after the buyer confirms what to buy, their name and email, and the payment method.",
        "x-openai-isConsequential": true,
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "package": {
                    "description": "Session to offer (natal = Christmas session 159€); or use amount_eur",
                    "type": "string",
                    "enum": [
                      "base",
                      "completo",
                      "equipa",
                      "familia",
                      "natal"
                    ]
                  },
                  "amount_eur": {
                    "description": "Amount to offer: 50, 100, 150, 200 or 250 (when not a specific session)",
                    "type": "integer",
                    "minimum": -9007199254740991,
                    "maximum": 9007199254740991
                  },
                  "buyer_name": {
                    "type": "string",
                    "minLength": 2,
                    "maxLength": 120
                  },
                  "buyer_email": {
                    "type": "string",
                    "format": "email",
                    "pattern": "^(?:[A-Za-z0-9_'+\\-]+\\.)*[A-Za-z0-9_'+\\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$",
                    "description": "Where the voucher is sent"
                  },
                  "buyer_phone": {
                    "description": "Mobile; required for MB WAY",
                    "type": "string",
                    "pattern": "^\\+?[0-9 ]{9,16}$"
                  },
                  "recipient_name": {
                    "description": "Who receives the gift (printed on the voucher)",
                    "type": "string",
                    "maxLength": 120
                  },
                  "message": {
                    "description": "Short dedication printed on the voucher",
                    "type": "string",
                    "maxLength": 240
                  },
                  "nif": {
                    "description": "Tax number for the invoice; optional",
                    "type": "string",
                    "pattern": "^[0-9A-Za-z]{9,14}$"
                  },
                  "payment_method": {
                    "type": "string",
                    "enum": [
                      "MBWAY",
                      "MB",
                      "CCARD"
                    ]
                  }
                },
                "required": [
                  "buyer_name",
                  "buyer_email",
                  "payment_method"
                ],
                "$schema": "http://json-schema.org/draft-07/schema#"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Result",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "422": {
            "description": "The request was understood but refused (e.g. time no longer free); `error` says why"
          }
        }
      }
    },
    "/api/tools/check_gift_voucher": {
      "post": {
        "operationId": "check_gift_voucher",
        "summary": "Ver saldo de vale",
        "description": "Checks a gift voucher or studio credit code (FSF-XXXX-XXXX): whether it is valid, its balance and expiry date.",
        "x-openai-isConsequential": false,
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "code": {
                    "type": "string",
                    "pattern": "^FSF-[A-Z0-9]{4}-[A-Z0-9]{4}$"
                  }
                },
                "required": [
                  "code"
                ],
                "$schema": "http://json-schema.org/draft-07/schema#"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Result",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "422": {
            "description": "The request was understood but refused (e.g. time no longer free); `error` says why"
          }
        }
      }
    },
    "/api/tools/start_caricature_booth_demo": {
      "post": {
        "operationId": "start_caricature_booth_demo",
        "summary": "Demo do Espelho Caricaturista",
        "description": "For event vendors (photobooth / magic mirror companies, wedding photographers, entertainment, event rental): starts a free demo of the Photobooth Caricatura software — a white-label Windows app that turns each guest photo into an AI caricature in seconds, delivered by QR code and printed with the vendor's own frame and logo. Creates a license with 10 free caricatures and emails the key and the quick-start guide; the studio then sends the software link and helps with the first setup. One demo per business email (asking again returns the same license). Only call after the user confirms the business name and email.",
        "x-openai-isConsequential": true,
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "business_name": {
                    "type": "string",
                    "minLength": 2,
                    "maxLength": 80,
                    "description": "Name of the vendor's business (shown on their license)"
                  },
                  "email": {
                    "type": "string",
                    "format": "email",
                    "pattern": "^(?:[A-Za-z0-9_'+\\-]+\\.)*[A-Za-z0-9_'+\\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$",
                    "description": "Business email: receives the license key and is used later to buy credits"
                  },
                  "phone": {
                    "description": "Mobile / WhatsApp, optional, to help with setup",
                    "type": "string",
                    "pattern": "^\\+?[0-9 ]{9,16}$"
                  },
                  "notes": {
                    "description": "Their setup (webcam, DSLR, magic mirror), events per month or questions; optional",
                    "type": "string",
                    "maxLength": 300
                  },
                  "language": {
                    "default": "pt",
                    "type": "string",
                    "enum": [
                      "pt",
                      "en",
                      "es",
                      "fr"
                    ]
                  }
                },
                "required": [
                  "business_name",
                  "email"
                ],
                "$schema": "http://json-schema.org/draft-07/schema#"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Result",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "422": {
            "description": "The request was understood but refused (e.g. time no longer free); `error` says why"
          }
        }
      }
    },
    "/api/tools/buy_caricature_booth_credits": {
      "post": {
        "operationId": "buy_caricature_booth_credits",
        "summary": "Comprar passes do Espelho Caricaturista",
        "description": "Buys event passes for the Photobooth Caricatura software license of an event vendor: each pass adds 600 AI caricatures for 39€ (no subscription, credits do not expire), 1 to 10 passes per purchase. Paid now by MB WAY, Multibanco or card; the caricatures are added to the license automatically as soon as the payment is confirmed and a confirmation email is sent. Needs the license key and the email it was registered with. Only call after the user confirms the number of passes, total price and payment method.",
        "x-openai-isConsequential": true,
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "license_key": {
                    "type": "string",
                    "pattern": "^PBP-[A-Z0-9]{4,16}-[A-Z0-9]{3,6}$",
                    "description": "License key, e.g. PBP-1A11ABE7C50-45B5"
                  },
                  "email": {
                    "type": "string",
                    "format": "email",
                    "pattern": "^(?:[A-Za-z0-9_'+\\-]+\\.)*[A-Za-z0-9_'+\\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$",
                    "description": "Email the license was registered with"
                  },
                  "passes": {
                    "default": 1,
                    "description": "Number of passes (600 caricatures each, 39€ each)",
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 10
                  },
                  "payment_method": {
                    "type": "string",
                    "enum": [
                      "MBWAY",
                      "MB",
                      "CCARD"
                    ]
                  },
                  "phone": {
                    "description": "Mobile; required for MB WAY",
                    "type": "string",
                    "pattern": "^\\+?[0-9 ]{9,16}$"
                  },
                  "nif": {
                    "description": "Company tax number (NIF/VAT) for the invoice; optional",
                    "type": "string",
                    "pattern": "^[0-9A-Za-z]{9,14}$"
                  }
                },
                "required": [
                  "license_key",
                  "email",
                  "payment_method"
                ],
                "$schema": "http://json-schema.org/draft-07/schema#"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Result",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "422": {
            "description": "The request was understood but refused (e.g. time no longer free); `error` says why"
          }
        }
      }
    },
    "/api/tools/check_caricature_booth_credits": {
      "post": {
        "operationId": "check_caricature_booth_credits",
        "summary": "Saldo do Espelho Caricaturista",
        "description": "Shows how many AI caricatures are left on an event vendor's Photobooth Caricatura license and how many were used. Needs the license key and the email it was registered with.",
        "x-openai-isConsequential": false,
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "license_key": {
                    "type": "string",
                    "pattern": "^PBP-[A-Z0-9]{4,16}-[A-Z0-9]{3,6}$",
                    "description": "License key, e.g. PBP-1A11ABE7C50-45B5"
                  },
                  "email": {
                    "type": "string",
                    "format": "email",
                    "pattern": "^(?:[A-Za-z0-9_'+\\-]+\\.)*[A-Za-z0-9_'+\\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"
                  }
                },
                "required": [
                  "license_key",
                  "email"
                ],
                "$schema": "http://json-schema.org/draft-07/schema#"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Result",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "422": {
            "description": "The request was understood but refused (e.g. time no longer free); `error` says why"
          }
        }
      }
    }
  }
}