Skip to content

Agendamentos ​

O agendamento tem duas camadas: o cabeçalho (quem, status, total, código legível para o cliente informar por telefone) e os itens — cada serviço do pacote com seu próprio horário e profissional: corte 10:00–10:30 com a Joana, barba 10:30–10:50 com a mesma Joana, ou manicure e pedicure simultâneas com pessoas diferentes.

Três decisões de design valem ouro na operação:

  • Congelamento: nome do serviço, preço e duração são copiados para o item no momento da criação. A tabela pode subir amanhã — o que foi combinado com aquele cliente não muda.
  • Janela bloqueada: cada item ocupa a agenda de startAt - bufferBefore até endAt + bufferAfter. O banco recusa sobreposição sobre essa janela, não só sobre o horário do serviço.
  • Trilha de eventos: toda transição registra status de origem/destino, quem fez (staff, cliente ou sistema) e uma nota. Cancelamentos viram discussão com o cliente — sem trilha você não tem resposta.

Headers obrigatórios

Todas as requisições devem incluir o header X-Booking-Id (UUID) e Authorization: Bearer {token} com o token de um usuário staff. A leitura é livre para qualquer papel; a escrita (criar e transicionar) exige owner, manager ou receptionist (403 FORBIDDEN caso contrário). Usuários professional veem apenas a própria agenda.


Máquina de Estados ​

O status do agendamento segue transições estritas — fora delas, a API responde 422 INVALID_STATUS_TRANSITION:

DeParaAçãoEfeito
(criação)pendingPOST /appointmentsStatus inicial quando a instância não confirma automaticamente ou algum serviço exige confirmação.
(criação)confirmedPOST /appointmentsQuando isAutoConfirm está ligado e nenhum serviço exige confirmação (confirmedAt preenchido).
pendingconfirmedPATCH .../confirmPreenche confirmedAt.
confirmedconfirmedPATCH .../check-inCliente chegou — preenche checkedInAt, sem mudar o status.
confirmedcompletedPATCH .../completeAtendimento concluído — preenche completedAt.
pending / confirmedcanceledPATCH .../cancelCancela em cascata (itens e recursos), registra motivo e autor.
confirmedno_showPATCH .../no-showFalta do cliente — só após o horário; incrementa totalNoShows.
pending / confirmed(novo agendamento)PATCH .../rescheduleCria um novo agendamento e cancela o antigo.

Endpoints ​

Listar Agendamentos ​

Lista paginada com filtros de período, status, origem e cliente — a base das telas de histórico.

http
GET /v1/booking/user/appointments

Parâmetros de Query ​

NomeTipoObrigatórioDescrição
page / perPagenumberNãoPaginação (máx. 100 por página).
fromstringNãoInício do período (YYYY-MM-DD), aplicado em startsAt.
tostringNãoFim do período (YYYY-MM-DD), aplicado em startsAt.
statusenumNãopending, confirmed, canceled, completed ou no_show.
sourceenumNãoonline, phone, walk_in, admin ou api.
bookingCustomerIdnumberNãoFiltra por cliente.
searchstringNãoBusca por código ou nome do convidado.
orderByenumNãoid, startsAt, createdAt ou totalPrice (padrão: startsAt).
orderDirectionenumNãoasc ou desc (padrão: desc).

Exemplo de Requisição ​

bash
curl -X GET "https://api.us.basis.tavares.in/v1/booking/user/appointments?from=2026-09-01&to=2026-09-30&status=confirmed" \
  -H "X-Booking-Id: 3fa85f64-5717-4562-b3fc-2c963f66afa6" \
  -H "Authorization: Bearer {token}" \
  -H "Accept: application/json"

Respostas ​

200 OK

Retorna a página de agendamentos com o cliente embutido em cada item.

json
{
  "meta": {
    "currentPage": 1,
    "perPage": 25
  },
  "data": [
    {
      "id": 1,
      "bookingId": 1,
      "code": "X7K2P9",
      "bookingCustomerId": 1,
      "guestName": null,
      "guestPhone": null,
      "guestEmail": null,
      "status": "confirmed",
      "source": "online",
      "startsAt": "2026-09-20T14:00:00.000-03:00",
      "endsAt": "2026-09-20T14:45:00.000-03:00",
      "totalPrice": "80.00",
      "totalDurationMinutes": 45,
      "createdAt": "2026-09-10T14:22:10.000+00:00",
      "updatedAt": "2026-09-10T14:22:10.000+00:00",
      "bookingCustomer": {
        "id": 1,
        "name": "Maria Silva"
      }
    }
  ]
}

Agenda do Intervalo ​

A consulta mais executada do módulo: a agenda do dia (ou da semana), ordenada por horário de início, com itens, profissionais, serviços e cliente embutidos — tudo em uma chamada.

http
GET /v1/booking/user/appointments/agenda

Parâmetros de Query ​

NomeTipoObrigatórioDescrição
fromstringSimInício do intervalo (YYYY-MM-DD).
tostringSimFim do intervalo (YYYY-MM-DD).
bookingProfessionalIdnumberNãoFiltra agendamentos que têm itens com o profissional (ignorado para usuários professional — o escopo deles é sempre a própria agenda).
statusenumNãoFiltra por status.

Exemplo de Requisição ​

bash
curl -X GET "https://api.us.basis.tavares.in/v1/booking/user/appointments/agenda?from=2026-09-20&to=2026-09-20" \
  -H "X-Booking-Id: 3fa85f64-5717-4562-b3fc-2c963f66afa6" \
  -H "Authorization: Bearer {token}" \
  -H "Accept: application/json"

Respostas ​

200 OK

Retorna o array de agendamentos do intervalo (sem paginação), ordenado por startsAt ascendente.

json
[
  {
    "id": 1,
    "code": "X7K2P9",
    "bookingCustomerId": 1,
    "status": "confirmed",
    "source": "online",
    "startsAt": "2026-09-20T14:00:00.000-03:00",
    "endsAt": "2026-09-20T14:45:00.000-03:00",
    "totalPrice": "80.00",
    "totalDurationMinutes": 45,
    "bookingCustomer": {
      "id": 1,
      "name": "Maria Silva",
      "phone": "11987654321"
    },
    "bookingAppointmentItems": [
      {
        "id": 1,
        "bookingServiceId": 1,
        "bookingProfessionalId": 1,
        "startAt": "2026-09-20T14:00:00.000-03:00",
        "endAt": "2026-09-20T14:45:00.000-03:00",
        "price": "80.00",
        "durationMinutes": 45,
        "serviceName": "Corte Masculino",
        "bookingProfessional": { "id": 1, "name": "Ana Souza" },
        "bookingService": { "id": 1, "name": "Corte Masculino" }
      }
    ]
  }
]

Criar Agendamento (Staff) ​

Criação manual pela recepção — telefone, walk-in, encaixe. Tudo roda em uma única transação: cabeçalho, itens, alocação de recursos e o primeiro evento.

O processo, item a item:

  1. Resolve o serviço (ativo) e o vínculo profissional ↔ serviço, aplicando preço e duração customizados quando existem.
  2. Calcula endAt (início + duração) e a janela bloqueada com buffers.
  3. Valida conflito na agenda do profissional (422 SLOT_UNAVAILABLE).
  4. Aloca o primeiro recurso obrigatório livre (422 RESOURCE_UNAVAILABLE se todos ocupados).
  5. Congela serviceName, price e durationMinutes no item.

O cabeçalho recebe um code legível único (6 caracteres, ex.: X7K2P9), startsAt/endsAt agregados dos itens, totais somados e o status inicial conforme a máquina de estados. Uma notificação de confirmação é enfileirada automaticamente.

http
POST /v1/booking/user/appointments

Payload (Body) ​

AtributoTipoObrigatórioDescrição
bookingCustomerIdnumberCondicionalCliente cadastrado (deve existir na instância). Obrigatório se não houver convidado.
guestNamestringCondicionalNome do convidado (máx. 150). Com convidado, exige guestPhone ou guestEmail.
guestPhonestringCondicionalTelefone do convidado (máx. 50).
guestEmailstringCondicionalE-mail do convidado (válido, máx. 200).
itemsarraySimPelo menos um item (ver abaixo).
items[].bookingServiceIdnumberSimServiço (deve existir e estar ativo na instância).
items[].bookingProfessionalIdnumberSimProfissional (deve existir, estar ativo e executar o serviço).
items[].startAtstringSimInício do atendimento, ISO 8601 com fuso (2026-09-20T14:00:00.000-03:00).
notesstringNãoObservações visíveis à equipe (máx. 10000 caracteres).

Exemplo de Requisição ​

bash
curl -X POST "https://api.us.basis.tavares.in/v1/booking/user/appointments" \
  -H "X-Booking-Id: 3fa85f64-5717-4562-b3fc-2c963f66afa6" \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -d '{
    "bookingCustomerId": 1,
    "items": [
      {
        "bookingServiceId": 1,
        "bookingProfessionalId": 1,
        "startAt": "2026-09-20T14:00:00.000-03:00"
      }
    ],
    "notes": "Cliente pediu atendimento com a Ana."
  }'

Respostas ​

201 Created

Retorna o ID e o código legível do agendamento.

json
{
  "id": 1,
  "code": "X7K2P9"
}

422 Unprocessable Entity

Conflito de horário (SLOT_UNAVAILABLE), recurso ocupado (RESOURCE_UNAVAILABLE), profissional que não executa o serviço (PROFESSIONAL_SERVICE_MISMATCH), cliente bloqueado (CUSTOMER_BLOCKED) ou identificação insuficiente (INVALID_CUSTOMER_IDENTITY).

json
{
  "message": "Horário indisponível para este profissional",
  "code": "SLOT_UNAVAILABLE"
}

Obter Agendamento por ID ​

Detalhe completo: itens (com profissional e serviço), trilha de eventos (com o usuário autor de cada transição) e cliente.

http
GET /v1/booking/user/appointments/:id

Respostas ​

200 OK

Retorna o agendamento com bookingAppointmentItems, bookingAppointmentEvents e bookingCustomer.

json
{
  "id": 1,
  "code": "X7K2P9",
  "status": "confirmed",
  "source": "online",
  "startsAt": "2026-09-20T14:00:00.000-03:00",
  "endsAt": "2026-09-20T14:45:00.000-03:00",
  "totalPrice": "80.00",
  "totalDurationMinutes": 45,
  "notes": "Cliente pediu atendimento com a Ana.",
  "internalNotes": null,
  "confirmedAt": "2026-09-10T14:22:10.000+00:00",
  "checkedInAt": null,
  "completedAt": null,
  "canceledAt": null,
  "bookingCustomer": {
    "id": 1,
    "name": "Maria Silva",
    "email": "maria.silva@email.com",
    "phone": "11987654321"
  },
  "bookingAppointmentItems": [
    {
      "id": 1,
      "startAt": "2026-09-20T14:00:00.000-03:00",
      "endAt": "2026-09-20T14:45:00.000-03:00",
      "price": "80.00",
      "durationMinutes": 45,
      "serviceName": "Corte Masculino",
      "bookingProfessional": { "id": 1, "name": "Ana Souza" },
      "bookingService": { "id": 1, "name": "Corte Masculino" }
    }
  ],
  "bookingAppointmentEvents": [
    {
      "id": 1,
      "fromStatus": "confirmed",
      "toStatus": "confirmed",
      "actorType": "customer",
      "bookingUserId": null,
      "note": "Agendamento criado",
      "createdAt": "2026-09-10T14:22:10.000+00:00",
      "bookingUser": null
    }
  ]
}

404 Not Found

Ocorre quando o agendamento não existe na instância.


Transições ​

Todas as transições são PATCH sem payload (exceto cancel e reschedule), respondem 204 No Content no sucesso e registram um evento na trilha. Fora da tabela de transições válidas, respondem 422 INVALID_STATUS_TRANSITION.

Confirmar ​

http
PATCH /v1/booking/user/appointments/:id/confirm

pending → confirmed, preenchendo confirmedAt.

Check-in ​

http
PATCH /v1/booking/user/appointments/:id/check-in

Marca a chegada do cliente (checkedInAt). O status permanece confirmed — o evento registra o check-in na trilha.

Concluir ​

http
PATCH /v1/booking/user/appointments/:id/complete

confirmed → completed, preenchendo completedAt. Alimenta a receita realizada nas métricas.

Cancelar ​

http
PATCH /v1/booking/user/appointments/:id/cancel
AtributoTipoObrigatórioDescrição
reasonstringNãoMotivo do cancelamento (máx. 255 caracteres) — vai para a trilha e para o agendamento.

Exemplo de Requisição ​

bash
curl -X PATCH "https://api.us.basis.tavares.in/v1/booking/user/appointments/1/cancel" \
  -H "X-Booking-Id: 3fa85f64-5717-4562-b3fc-2c963f66afa6" \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -d '{ "reason": "Cliente pediu para cancelar" }'

Cancelamento em cascata

O cancelamento marca canceledAt no agendamento, em todos os itens e nos vínculos de recursos — liberando a agenda do profissional e a sala/cadeira na mesma transação. Uma notificação de cancelamento é enfileirada. O staff cancela a qualquer momento; o prazo cancellationDeadlineMinutes só é aplicado no portal do cliente.

No-show ​

http
PATCH /v1/booking/user/appointments/:id/no-show

confirmed → no_show, somente depois do horário do agendamento (422 APPOINTMENT_NOT_STARTED antes disso). Incrementa totalNoShows do cliente.

Reagendar ​

http
PATCH /v1/booking/user/appointments/:id/reschedule

Cria um novo agendamento (mesma identidade e serviços, novos horários), cancela o antigo com motivo "Reagendado" e registra eventos nos dois. O novo aponta para o antigo em rebookingAppointmentId — a cadeia de reagendamentos fica auditável.

Payload (Body) ​

AtributoTipoObrigatórioDescrição
itemsarraySimOs novos horários — mesmo formato dos itens da criação.

Exemplo de Requisição ​

bash
curl -X PATCH "https://api.us.basis.tavares.in/v1/booking/user/appointments/1/reschedule" \
  -H "X-Booking-Id: 3fa85f64-5717-4562-b3fc-2c963f66afa6" \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -d '{
    "items": [
      {
        "bookingServiceId": 1,
        "bookingProfessionalId": 1,
        "startAt": "2026-09-22T15:00:00.000-03:00"
      }
    ]
  }'

Respostas ​

200 OK

Retorna o ID e o código do novo agendamento (o antigo foi cancelado).

json
{
  "id": 2,
  "code": "M4T8QW"
}

422 Unprocessable Entity

Ocorre quando o novo horário conflita (SLOT_UNAVAILABLE) ou o agendamento não está em pending/confirmed (INVALID_STATUS_TRANSITION).