Files
ealmeida eae213c7f0 feat(wordpress): nova skill mcp-fluent-booking - MCP dedicado FluentBooking/FluentBooking Pro
Cobre: calendarios, event types, reservas (listar/detalhe/mudar
estado/reenviar confirmacao), actividade, disponibilidade, reports,
definicoes gerais, anfitrioes, estado do plugin/licenca Pro. 15
tools via REST API real com Application Password (nao SSH/SQL como
mcp-bit-social - o FluentBooking regista register_rest_route com
permission_callback por capability). Testado ponta-a-ponta em
producao 19-08-2026, incluindo escrita sem mutar dados reais (422
'No changes found' do proprio plugin). references/api-reference.md
com os endpoints/filtros/enums confirmados e references/diagnostics.md
com o playbook de uso (agenda, cancelamento, health check).
2026-08-19 05:29:42 +01:00

5.7 KiB

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=<slug>), 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.<dia>.{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:<estado>, 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}.