# FluentBooking — referência da REST API (confirmado em produção, 2026-08-19) Namespace: `fluent-booking/v2` (WP REST API real, `wp-json/fluent-booking/v2/...`). Auth: Basic Auth com Application Password — o `permission_callback` de cada rota é uma `Policy` que verifica `current_user_can()`/`PermissionManager`, não nonce de sessão. 100 rotas registadas entre free e Pro; este ficheiro documenta só as usadas pelo MCP. ## Calendários (`/calendars`) `GET /calendars` — lista todos, cada um com `slots[]` (event types) aninhados, incluindo `settings.weekly_schedules` completo por dia. `GET /calendars/{id}` — um só, mesma forma. Campos relevantes de um calendar: `id`, `title`, `slug`, `status` (`active`/…), `type` (`simple`/…), `visibility` (`public`/…), `author_timezone`, `public_url` (`https://descomplicar.pt/?fluent-booking=calendar&host=`), `author_profile` (nome, avatar, telefone). ## Event types (`/events/{event_id}`, aninhado em `calendars[].slots[]`) `GET /events/{event_id}` → `{calendar_event: {...}}`. Campos: `id`, `calendar_id`, `duration` (minutos), `title`, `slug`, `description` (HTML), `settings.schedule_type` (`weekly_schedules`/…), `settings.weekly_schedules..{enabled, slots:[{start,end}]}`. **Gotcha de nomes:** `GET /calendars/event-lists` (sem id) devolve sempre `{"code":"plugin_exception", ...}` em produção — não é um bug do MCP, o controller `CalendarController@getCalendarEventLists` está partido standalone nesta instalação. Não usar este endpoint; listar event types via `fb_list_calendars`/`fb_get_calendar` (já vêm aninhados). ## Reservas (`/schedules`) `GET /schedules` — parâmetros via `filters[...]` (bracket notation, o MCP trata isto automaticamente a partir de campos planos no input schema): | Filtro | Valores | Nota | |---|---|---| | `filters[period]` | `upcoming` (omissão), `completed`, `cancelled`, `pending`, `no_show`, `latest_bookings`, `all` | Valor fora da lista cai silenciosamente para `upcoming` | | `filters[author]` | id numérico do calendário, `me`, `all` | Sem permissão "ver todas as reservas", o utilizador só vê as suas mesmo que peça `all` | | `filters[event]` | id numérico do event type | | | `filters[event_type]` | `single`/`group`/… | | | `filters[email]` | email do convidado | | | `filters[range]` | `[date_from, date_to]` | Filtra por `created_at`, não pela hora da reunião | | `search` | texto livre | nome/email do convidado | | `page` | inteiro | paginação Laravel-style | Forma de uma reserva (`schedules.data[]`): `id`, `hash`, `calendar_id`, `event_id`, `group_id` (agrupa reservas de grupo), `host_user_id`, `start_time`/`end_time` (timezone do site), `first_name`/`last_name`/`email`/`phone`/`message`, `location_details` (`{type, online_meeting_link}` ou presencial), `status` (`scheduled`/`completed`/`cancelled`/`rejected`/`no_show`), `payment_status`, `source_url` (página onde o convidado marcou), `custom_form_data` (campos custom do formulário de marcação, por chave), `calendar_event` (event type completo aninhado, redundante mas presente sempre), `happening_status`, `booking_status_text`, `reschedule_url`. `GET /schedules/{id}` — mesma forma para uma reserva. `PUT /schedules/{id}` (`fb_update_booking_status`) — body `{column:"status", value:, cancel_reason?, reject_reason?, refund_payment?}`. Colunas válidas no controller real: `internal_note`, `email`, `phone`, `first_name`, `last_name`, `status`, `payment_status` — o MCP só expõe `status` deliberadamente (as outras deixam editar dados do convidado sem justificação de negócio clara; ver SKILL.md "Fora do âmbito"). Side-effects reais confirmados no código (`SchedulesController::patchBooking`): - `status=cancelled` → `Booking::cancelMeeting($reason, 'host', $userId)` + email de cancelamento. - `status=rejected` → `Booking::rejectMeeting($reason, $userId)`. - `status=scheduled` com `payment_method`+`payment_order` associados → marca a order como `paid`. - Valor igual ao actual → `422 {"message":"No changes found"}` (não é um bug, é a validação do próprio plugin — útil para testar o caminho de escrita sem mutar nada). `POST /schedules/{id}/send-confirmation-email` (`fb_send_confirmation_email`) — sem body, envia email real. ## Actividade (`/schedules/{id}/activities`, `/reports/activities`) Mesma forma nos dois: `{id, booking_id, parent_id, created_by, status, type, title, description, created_at, updated_at}`. `/reports/activities` é o log do site inteiro (todas as reservas); `/schedules/{id}/activities` filtra a uma só. ## Disponibilidade (`/availability`) `GET /availability` — lista templates reutilizáveis (`{id, host_name, title, usage_count, settings:{timezone, date_overrides[], weekly_schedules}}`). `GET /availability/{id}` — um só, envelope `{schedule: {...}}` em vez de `{availabilities: {data:[...]}}`. ## Reports (`/reports`, `/reports/graph-reports`) `GET /reports` → `{overview: [{title, period?, number, content, stat}], latest_books: [...]}`. Métricas confirmadas: Total Bookings, Completed Bookings, Cancelled Bookings, Total Guests. `GET /reports/graph-reports?date_range[0]=YYYY-MM-DD&date_range[1]=YYYY-MM-DD` → `{booked_stats, completed_stats, cancelled_stats}`, cada um `{"YYYY-MM-DD": count}`. Sem `date_range`, o controller usa uma janela por omissão (~7 dias, granularidade adaptada ao intervalo — dia/semana/mês). ## Definições e equipa `GET /settings/general` → `{payments, emailing, administration}` (moeda, remetente/rodapé de email, `auto_cancel_timing`, dia de início da semana). `GET /admin/all-hosts` → `{hosts: [{id, name, label, avatar, calendar_id, deleted_user}]}`. `GET /settings/license` (Pro) → `{status, variation_title, expires, is_expired, activation_hash, renew_url, purchase_url}`.