
# Agendamentos

A API de Agendamentos permite que você marque compromissos para seus contatos em seus tipos de evento e, em seguida, busque, liste, atualize, cancele ou exclua esses compromissos. Ela também responde à pergunta que surge primeiro na maioria dos fluxos de agendamento — quais horários estão realmente livres — e cobre o lado do calendário: listando os Google Calendars que você conectou e importando eventos que já existem neles. Quando uma conexão com o Google Calendar está ativa, o evento de calendário correspondente é criado e mantido sincronizado automaticamente em segundo plano. Restaurantes que usam Zenchef ou Formitable para seus próprios sistemas de reserva também podem ser verificados e conectados aqui, para que o Agente de IA reserve mesas reais em vez de compromissos internos.

Todos os caminhos nesta página são relativos à URL base `https://api.youraiconnector.com/v1`. Cada solicitação precisa da sua chave de API — consulte [Autenticação](authentication.md) para obter a lista completa de formas de enviá-la. Os exemplos abaixo usam o cabeçalho `X-API-Key`, com um exemplo cURL mostrando também o formato de consulta `?apiKey=`.

> **Eventos vs. agendamentos:** Um *tipo de evento* é uma definição de intervalo reservável (o tipo de reunião, sua duração, suas salas). Um *agendamento* é uma instância reservada de um tipo de evento para um contato específico. Você reserva um agendamento referenciando o contato e o tipo de evento.

---

## O objeto de agendamento

Cada endpoint que retorna um agendamento usa a mesma estrutura:

| Campo | Descrição |
|---|---|
| `id` | ID exclusivo do agendamento. |
| `contact_id` | ID do contato com o qual o agendamento foi marcado. |
| `event_id` | ID do tipo de evento no qual o agendamento foi marcado. |
| `status` | `Confirmed` ou `Canceled`. |
| `start_time` | Início do agendamento, ISO 8601 em UTC. |
| `end_time` | Fim do agendamento, ISO 8601 em UTC. |
| `created_at` | Quando o agendamento foi criado. |
| `last_modified_at` | Quando o agendamento foi alterado pela última vez. |
| `room_name` | Sala ou recurso no qual o agendamento foi marcado, quando o tipo de evento usa salas. |
| `description` | Descrição de formato livre do agendamento. |
| `summary` | Resumo ou título curto. |
| `cancelation_reason` | Motivo fornecido quando o agendamento foi cancelado, se houver. |
| `google_calendar_event_id` | ID do evento vinculado do Google Agenda. Definido assim que a sincronização do calendário for concluída; `null` quando nenhum calendário estiver conectado ou enquanto a sincronização ainda estiver em andamento. |
| `calendar_synced` | `true` assim que o agendamento estiver vinculado a um evento de calendário. |
| `imported` | `true` quando o agendamento foi importado de um calendário externo em vez de reservado diretamente. |
| `is_recurring` | `true` quando o agendamento faz parte de uma série recorrente. |
| `recurrence_frequency` | Com que frequência o agendamento se repete, quando recorrente. |
| `recurring_event_id` | ID da série recorrente à qual este agendamento pertence. |
| `recurring_interval` | Intervalo entre repetições, quando recorrente. |
| `recurring_sequence` | Posição deste agendamento dentro de sua série recorrente. |
| `end_after_x_occurrences` | Número de ocorrências após as quais a série recorrente termina. |
| `booking_provider` | Sistema de origem de onde veio a reserva, quando feita por meio de um provedor de reserva conectado. |

> **Sobre a sincronização de calendário:** Logo após você reservar ou alterar um agendamento, `google_calendar_event_id` pode ainda ser `null` e `calendar_synced` pode ser `false` porque a sincronização é executada em segundo plano um momento depois. Busque o agendamento novamente pouco tempo depois para ver os campos de calendário preenchidos.

---

## Encontrar horários disponíveis

`GET /appointments/available-slots`

Retorna os horários que estão genuinamente livres em um tipo de evento entre dois momentos. Esta é normalmente a **primeira** chamada em um fluxo de agendamento: mostre esses horários, deixe a pessoa escolher um e, em seguida, envie o horário escolhido para [Agendar um compromisso](#book-an-appointment).

A resposta já leva em conta o horário de funcionamento e a duração do intervalo do próprio tipo de evento, suas salas, compromissos que você já agendou nele e tudo o que está bloqueado nos Google Calendars conectados — portanto, um horário que aparece aqui é um que você pode reservar.

| Parâmetro de consulta | Obrigatório | Descrição |
|---|---|---|
| `event_id` | Sim | O tipo de evento a ser verificado. Deve pertencer à sua conta. |
| `start_time` | Sim | Início da janela para a qual você deseja horários, data e hora em ISO 8601. |
| `end_time` | Sim | Fim da janela, data e hora em ISO 8601. O dia final completo está incluído. |

Os resultados são retornados agrupados por dia — e, quando o tipo de evento usa salas, um grupo por sala por dia:

| Campo | Descrição |
|---|---|
| `date` | O dia que o grupo cobre, escrito `DD/MM/YYYY`. |
| `day` | Nome do dia da semana em letras minúsculas, por exemplo `monday`. |
| `room_name` | A sala ou recurso ao qual este grupo pertence, quando o tipo de evento usa salas. |
| `available_slots` | Os blocos reserváveis naquele dia, do mais cedo para o mais tarde. |

Cada entrada em `available_slots` possui:

| Campo | Descrição |
|---|---|
| `start_time` | Início do bloco como `HH:mm`. |
| `end_time` | Fim do bloco como `HH:mm`. |
| `available` | `true` — apenas o tempo livre é retornado. |
| `spots_left` | Quantas reservas ainda cabem neste bloco. Presente apenas em tipos de evento que aceitam mais de uma reserva por intervalo. |

> **Os horários são locais ao tipo de evento, não UTC.** `date`, `start_time` e `end_time` são valores de relógio de parede no fuso horário do próprio tipo de evento (sua substituição, ou o fuso horário da sua conta quando não houver nenhum). [Agendar um compromisso](#book-an-appointment) espera um instante UTC em ISO 8601, portanto, converta o horário que você escolheu antes de enviá-lo.

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/appointments/available-slots?event_id=event_xyz789&start_time=2026-06-15T00:00:00.000Z&end_time=2026-06-19T00:00:00.000Z" \
  -H "X-API-Key: YOUR_API_KEY"
```

**JavaScript**

```javascript
const params = new URLSearchParams({
  event_id: "event_xyz789",
  start_time: "2026-06-15T00:00:00.000Z",
  end_time: "2026-06-19T00:00:00.000Z",
});
const res = await fetch(
  `https://api.youraiconnector.com/v1/appointments/available-slots?${params}`,
  { headers: { "X-API-Key": "YOUR_API_KEY" } }
);
const data = await res.json();
console.log(data.data);
```

**Python**

```python
import requests

res = requests.get(
    "https://api.youraiconnector.com/v1/appointments/available-slots",
    headers={"X-API-Key": "YOUR_API_KEY"},
    params={
        "event_id": "event_xyz789",
        "start_time": "2026-06-15T00:00:00.000Z",
        "end_time": "2026-06-19T00:00:00.000Z",
    },
)
print(res.json()["data"])
```

**Resposta** (`200 OK`):

```json
{
  "success": true,
  "data": [
    {
      "date": "15/06/2026",
      "day": "monday",
      "room_name": "Room A",
      "available_slots": [
        { "start_time": "10:00", "end_time": "10:30", "available": true },
        { "start_time": "10:30", "end_time": "11:00", "available": true }
      ]
    },
    {
      "date": "16/06/2026",
      "day": "tuesday",
      "room_name": "Room A",
      "available_slots": [
        { "start_time": "09:00", "end_time": "09:30", "available": true, "spots_left": 2 }
      ]
    }
  ]
}
```

Um dia sem nada livre simplesmente não aparece. A falta de `event_id`, `start_time` ou `end_time` retorna `400`; um tipo de evento que não está na sua conta retorna `404`.

---

## Reservar um agendamento

`POST /appointments`

Reserva um novo agendamento para um contato em um dos seus tipos de evento. O horário de término é calculado automaticamente a partir da duração do intervalo do tipo de evento.

A reserva passa por uma verificação de conflito: se o intervalo solicitado sobrepuser um agendamento confirmado existente no mesmo tipo de evento, a solicitação falhará com um `409` e nada será criado.

| Campo | Obrigatório | Descrição |
|---|---|---|
| `contact_id` | Sim | ID do contato para o qual reservar. Deve pertencer à sua conta. |
| `event_id` | Sim | ID do tipo de evento no qual reservar. Deve pertencer à sua conta. |
| `start_time` | Sim | Início desejado como uma data-hora ISO 8601. |
| `room_name` | Não | Nome da sala ou recurso, quando o tipo de evento usa salas. |

**cURL** (usando o formato de consulta `?apiKey=`)

```bash
curl -X POST "https://api.youraiconnector.com/v1/appointments?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "contact_id": "contact_abc123",
    "event_id": "event_xyz789",
    "start_time": "2026-06-15T10:00:00.000Z",
    "room_name": "Room A"
  }'
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/appointments", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    contact_id: "contact_abc123",
    event_id: "event_xyz789",
    start_time: "2026-06-15T10:00:00.000Z",
    room_name: "Room A",
  }),
});
const data = await res.json();
console.log(data.appointment_id);
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/appointments",
    headers={
        "X-API-Key": "YOUR_API_KEY",
        "Content-Type": "application/json",
    },
    json={
        "contact_id": "contact_abc123",
        "event_id": "event_xyz789",
        "start_time": "2026-06-15T10:00:00.000Z",
        "room_name": "Room A",
    },
)
print(res.json()["appointment_id"])
```

**Resposta** (`201 Created`):

```json
{
  "success": true,
  "appointment_id": "aBcD1234eFgH5678",
  "appointment": {
    "id": "aBcD1234eFgH5678",
    "contact_id": "contact_abc123",
    "event_id": "event_xyz789",
    "status": "Confirmed",
    "start_time": "2026-06-15T10:00:00.000Z",
    "end_time": "2026-06-15T10:30:00.000Z",
    "created_at": "2026-06-10T09:00:00.000Z",
    "last_modified_at": "2026-06-10T09:00:00.000Z",
    "room_name": "Room A",
    "google_calendar_event_id": null,
    "calendar_synced": false
  }
}
```

---

## Obter um agendamento

`GET /appointments/{appointmentId}`

Retorna um único agendamento pelo seu ID, incluindo seu estado de sincronização de calendário.

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/appointments/aBcD1234eFgH5678" \
  -H "X-API-Key: YOUR_API_KEY"
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/appointments/aBcD1234eFgH5678",
  { headers: { "X-API-Key": "YOUR_API_KEY" } }
);
const data = await res.json();
console.log(data.appointment);
```

**Python**

```python
import requests

res = requests.get(
    "https://api.youraiconnector.com/v1/appointments/aBcD1234eFgH5678",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
print(res.json()["appointment"])
```

**Resposta** (`200 OK`):

```json
{
  "success": true,
  "appointment": {
    "id": "aBcD1234eFgH5678",
    "contact_id": "contact_abc123",
    "event_id": "event_xyz789",
    "status": "Confirmed",
    "start_time": "2026-06-15T10:00:00.000Z",
    "end_time": "2026-06-15T10:30:00.000Z",
    "room_name": "Room A",
    "google_calendar_event_id": "abc123googleevent",
    "calendar_synced": true
  }
}
```

---

## Listar agendamentos

`GET /appointments`

Lista os agendamentos da sua conta, do mais recente para o mais antigo, com paginação baseada em cursor.

| Parâmetro de consulta | Obrigatório | Descrição |
|---|---|---|
| `contact_id` | Não | Retorna apenas agendamentos para este contato. Listagens filtradas por contato incluem **apenas agendamentos confirmados**. |
| `date` | Não | Retorna apenas agendamentos neste dia do calendário (`YYYY-MM-DD`). **Requer `contact_id`.** |
| `status` | Não | Filtra por `Confirmed` ou `Canceled`. Disponível apenas **sem** `contact_id`. |
| `limit` | Não | Tamanho da página, um número inteiro entre 1 e 100. O padrão é `50`. |
| `cursor` | Não | O valor `next_cursor` de uma resposta anterior. |

Algumas regras para ter em mente:

- **Sem filtros**, você obtém todos os agendamentos da conta, página por página.
- **Por contato** — defina `contact_id` para ver os agendamentos confirmados de um contato. Você pode restringir isso a um único dia passando também `date`.
- **Por status** — defina `status` (sem `contact_id`) para listar apenas agendamentos `Confirmed` ou apenas `Canceled` em toda a conta.
- O filtro `date` sem `contact_id`, ou `status=Canceled` junto com `contact_id`, retorna um `400`.

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/appointments?contact_id=contact_abc123&date=2026-06-15" \
  -H "X-API-Key: YOUR_API_KEY"
```

**JavaScript**

```javascript
const params = new URLSearchParams({
  contact_id: "contact_abc123",
  date: "2026-06-15",
});
const res = await fetch(
  `https://api.youraiconnector.com/v1/appointments?${params}`,
  { headers: { "X-API-Key": "YOUR_API_KEY" } }
);
const data = await res.json();
console.log(data.appointments, data.next_cursor);
```

**Python**

```python
import requests

res = requests.get(
    "https://api.youraiconnector.com/v1/appointments",
    headers={"X-API-Key": "YOUR_API_KEY"},
    params={"contact_id": "contact_abc123", "date": "2026-06-15"},
)
data = res.json()
print(data["appointments"], data["next_cursor"])
```

**Resposta** (`200 OK`):

```json
{
  "success": true,
  "appointments": [
    {
      "id": "aBcD1234eFgH5678",
      "contact_id": "contact_abc123",
      "event_id": "event_xyz789",
      "status": "Confirmed",
      "start_time": "2026-06-15T10:00:00.000Z",
      "end_time": "2026-06-15T10:30:00.000Z",
      "calendar_synced": true
    }
  ],
  "next_cursor": null
}
```

Para navegar pelos resultados, passe o `next_cursor` de uma resposta como o `cursor` da próxima solicitação. Continue até que `next_cursor` seja `null`. Consulte [Erros e Paginação](errors-and-pagination.md) para o padrão de paginação compartilhado.

---

## Atualizar um agendamento

`PUT /appointments/{appointmentId}`

Remarque um agendamento ou altere seus detalhes. Envie apenas os campos que deseja alterar — pelo menos um é obrigatório. O início e o fim combinados devem permanecer em ordem cronológica (`end_time` deve ser posterior a `start_time`). As alterações são sincronizadas automaticamente com o evento de calendário vinculado.

| Campo | Descrição |
|---|---|
| `start_time` | Nova data e hora de início, no formato ISO 8601. |
| `end_time` | Nova data e hora de término, no formato ISO 8601. Deve ser posterior ao horário de início. |
| `room_name` | Novo nome da sala ou recurso. |
| `description` | Nova descrição, ou `null` para limpá-la. |
| `summary` | Novo resumo, ou `null` para limpá-lo. |

**cURL**

```bash
curl -X PUT "https://api.youraiconnector.com/v1/appointments/aBcD1234eFgH5678" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "start_time": "2026-06-16T10:00:00.000Z",
    "end_time": "2026-06-16T10:30:00.000Z"
  }'
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/appointments/aBcD1234eFgH5678",
  {
    method: "PUT",
    headers: {
      "X-API-Key": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      start_time: "2026-06-16T10:00:00.000Z",
      end_time: "2026-06-16T10:30:00.000Z",
    }),
  }
);
const data = await res.json();
console.log(data.appointment);
```

**Python**

```python
import requests

res = requests.put(
    "https://api.youraiconnector.com/v1/appointments/aBcD1234eFgH5678",
    headers={
        "X-API-Key": "YOUR_API_KEY",
        "Content-Type": "application/json",
    },
    json={
        "start_time": "2026-06-16T10:00:00.000Z",
        "end_time": "2026-06-16T10:30:00.000Z",
    },
)
print(res.json()["appointment"])
```

**Resposta** (`200 OK`):

```json
{
  "success": true,
  "appointment_id": "aBcD1234eFgH5678",
  "appointment": {
    "id": "aBcD1234eFgH5678",
    "contact_id": "contact_abc123",
    "event_id": "event_xyz789",
    "status": "Confirmed",
    "start_time": "2026-06-16T10:00:00.000Z",
    "end_time": "2026-06-16T10:30:00.000Z",
    "calendar_synced": true
  }
}
```

---

## Cancelar um agendamento

`POST /appointments/{appointmentId}/cancel`

Cancela um agendamento confirmado, registrando opcionalmente um motivo. O agendamento permanece em sua conta com o status `Canceled`, e o evento de calendário vinculado é removido automaticamente em segundo plano. Cancelar um agendamento já cancelado retorna um `400`.

| Campo | Obrigatório | Descrição |
|---|---|---|
| `cancellation_reason` | Não | Motivo do cancelamento, armazenado no agendamento. |

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/appointments/aBcD1234eFgH5678/cancel" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "cancellation_reason": "Client asked to reschedule next month"
  }'
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/appointments/aBcD1234eFgH5678/cancel",
  {
    method: "POST",
    headers: {
      "X-API-Key": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      cancellation_reason: "Client asked to reschedule next month",
    }),
  }
);
const data = await res.json();
console.log(data.success);
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/appointments/aBcD1234eFgH5678/cancel",
    headers={
        "X-API-Key": "YOUR_API_KEY",
        "Content-Type": "application/json",
    },
    json={"cancellation_reason": "Client asked to reschedule next month"},
)
print(res.json()["success"])
```

**Resposta** (`200 OK`):

```json
{
  "success": true,
  "appointment_id": "aBcD1234eFgH5678"
}
```

---

## Excluir um agendamento

`DELETE /appointments/{appointmentId}`

Exclui permanentemente um agendamento e suas referências. Se você deseja apenas cancelar a reserva mantendo o registro, use [cancelar](#cancel-an-appointment) em vez disso.

**cURL**

```bash
curl -X DELETE "https://api.youraiconnector.com/v1/appointments/aBcD1234eFgH5678" \
  -H "X-API-Key: YOUR_API_KEY"
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/appointments/aBcD1234eFgH5678",
  { method: "DELETE", headers: { "X-API-Key": "YOUR_API_KEY" } }
);
const data = await res.json();
console.log(data.success);
```

**Python**

```python
import requests

res = requests.delete(
    "https://api.youraiconnector.com/v1/appointments/aBcD1234eFgH5678",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
print(res.json()["success"])
```

**Resposta** (`200 OK`):

```json
{
  "success": true
}
```

---

## Listar seus Google Calendars conectados

`GET /appointments/google-calendars`

Retorna os Google Calendars disponíveis nesta conta, diretamente do Google — útil para mostrar ao titular da conta um seletor de qual calendário importar abaixo, ou apenas para confirmar que a conexão está ativa.

Isso só funciona depois que a conta tiver conectado o Google Calendar (Configurações → Integrações) com pelo menos acesso de leitura. Se não tiver, ou se o acesso concedido não incluir mais o escopo de leitura de calendário, você receberá um `400` solicitando que você o conecte (ou reconecte).

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/appointments/google-calendars" \
  -H "X-API-Key: YOUR_API_KEY"
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/appointments/google-calendars", {
  headers: { "X-API-Key": "YOUR_API_KEY" },
});
const data = await res.json();
console.log(data.data);
```

**Python**

```python
import requests

res = requests.get(
    "https://api.youraiconnector.com/v1/appointments/google-calendars",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
print(res.json()["data"])
```

**Resposta** (`200 OK`):

```json
{
  "success": true,
  "data": [
    {
      "id": "primary",
      "summary": "jane@example.com",
      "timeZone": "America/New_York",
      "accessRole": "owner",
      "primary": true
    },
    {
      "id": "abcdefg1234567890@group.calendar.google.com",
      "summary": "Bookings",
      "timeZone": "America/New_York",
      "accessRole": "writer"
    }
  ]
}
```

Cada entrada segue o formato [`CalendarListEntry`](https://developers.google.com/calendar/api/v3/reference/calendarList) do próprio Google, portanto, os nomes dos campos seguem a `camelCase` do Google, não o `snake_case` usual desta API — esses são dados do Google passados como estão, não os nossos. Uma conexão ausente ou revogada retorna `400` com um erro explicando que o Google Calendar precisa ser conectado (ou reconectado).

---

## Importar eventos de um Google Calendar

`POST /appointments/import-calendar-events`

Puxa os eventos que já estão no(s) Google Calendar(s) conectado(s) de uma campanha ou Agente de IA e os transforma em agendamentos — útil na primeira vez que você conecta um calendário que já possui reservas. Isso pode levar algum tempo (cada evento passa por uma extração para descobrir para quem é), por isso nunca é executado em linha: a solicitação enfileira um trabalho em segundo plano e retorna um `job_id` para consulta.

| Campo | Obrigatório | Descrição |
|---|---|---|
| `campaign_id` | Um destes dois | A campanha cujo(s) calendário(s) conectado(s) será(ão) usado(s) para importação. |
| `agent_id` | Um destes dois | O Agente de IA cujo(s) calendário(s) conectado(s) será(ão) usado(s) para importação. |
| `identifier` | Sim | `"EMAIL"` ou `"PHONE_NUMBER"` — qual informação de contato extrair de cada evento de calendário para corresponder ou criar o contato ao qual ele pertence. |

Envie exatamente um entre `campaign_id` / `agent_id`, nunca ambos e nunca nenhum — qualquer combinação retorna um `400`. O que você enviar deve pertencer à sua conta, caso contrário, você receberá um `404`.

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/appointments/import-calendar-events?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "agent_id": "agent_abc123",
    "identifier": "EMAIL"
  }'
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/appointments/import-calendar-events", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    agent_id: "agent_abc123",
    identifier: "EMAIL",
  }),
});
const data = await res.json();
console.log(data.job_id);
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/appointments/import-calendar-events",
    headers={
        "X-API-Key": "YOUR_API_KEY",
        "Content-Type": "application/json",
    },
    json={"agent_id": "agent_abc123", "identifier": "EMAIL"},
)
print(res.json()["job_id"])
```

**Resposta** (`202 Accepted`):

```json
{
  "success": true,
  "job_id": "jK9mQ2xR7pL4wN1t",
  "status": "queued",
  "campaign_id": null,
  "agent_id": "agent_abc123"
}
```

`campaign_id` e `agent_id` retornam o que você enviou; o outro é sempre `null`.

### Consultar o trabalho de importação

`GET /appointments/import-calendar-events/{jobId}`

```bash
curl "https://api.youraiconnector.com/v1/appointments/import-calendar-events/jK9mQ2xR7pL4wN1t" \
  -H "X-API-Key: YOUR_API_KEY"
```

**Resposta** (`200 OK`):

```json
{
  "success": true,
  "job_id": "jK9mQ2xR7pL4wN1t",
  "status": "completed",
  "message": "Imported 12 events as appointments.",
  "error": null
}
```

| `status` | Significado |
|---|---|
| `queued` | Ainda não iniciado. Continue consultando. |
| `processing` | A importação está em execução. Continue consultando. |
| `completed` | Concluído — `message` contém um breve resumo legível. |
| `failed` | Algo deu errado — `error` contém o motivo. |

`GET` em um `jobId` que não existe (ou pertence a uma conta diferente) retorna `404`.

---

## Integrações de reserva de restaurantes (Zenchef / Formitable)

Zenchef e Formitable são sistemas de reserva de restaurantes pelos quais seu Agente de IA pode reservar mesas reais. Cada um possui um **widget de reserva público e não autenticado** (`https://api.youraiconnector.com/v1/zenchef-widget/...` e `https://api.youraiconnector.com/v1/formitable-widget/...`) que é renderizado dentro do chat para o cliente — essas rotas de widget são páginas HTML simples destinadas a serem abertas em um navegador, não endpoints de API JSON, portanto, não estão documentadas aqui. O que se segue são os endpoints de gerenciamento de conta: verificar se um ID de restaurante pertence ao titular da conta e, em seguida, adicioná-lo, atualizá-lo ou removê-lo.

### Zenchef

Conectar um restaurante Zenchef é uma verificação de duas etapas, para que o titular da conta prove que realmente administra o restaurante antes que ele seja conectado ao bot: primeiro, verifique se o ID existe (sem revelar o nome), depois peça que eles mesmos digitem o nome do restaurante e verifique se corresponde.

**Etapa 1 — Verificar se um ID de restaurante existe**

`POST /appointments/zenchef-restaurants/check`

| Campo | Obrigatório | Descrição |
|---|---|---|
| `restaurant_id` | Sim | O ID do restaurante Zenchef a ser verificado. |

```bash
curl -X POST "https://api.youraiconnector.com/v1/appointments/zenchef-restaurants/check?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "restaurant_id": "12345" }'
```

**Resposta** (`200 OK`):

```json
{
  "success": true,
  "data": { "exists": true, "requiresNameVerification": true }
}
```

`exists: false` significa que nenhum restaurante Zenchef possui esse ID — nada mais a fazer. Limitado a 10 verificações a cada 5 minutos por conta; exceder esse limite retorna `429`.

**Etapa 2 — Verificar o nome do restaurante**

`POST /appointments/zenchef-restaurants/verify-name`

| Campo | Obrigatório | Descrição |
|---|---|---|
| `restaurant_id` | Sim | O ID do restaurante Zenchef da etapa 1. |
| `user_input_name` | Sim | O nome que o titular da conta digitou — comparado com o nome real do restaurante no Zenchef (insensível a maiúsculas/minúsculas e espaços em branco). |

```bash
curl -X POST "https://api.youraiconnector.com/v1/appointments/zenchef-restaurants/verify-name?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "restaurant_id": "12345", "user_input_name": "The Blue Door Bistro" }'
```

**Resposta** (`200 OK`):

```json
{
  "success": true,
  "data": {
    "verified": true,
    "restaurantDetails": {
      "id": "12345",
      "name": "The Blue Door Bistro",
      "address": "1 Rue de Rivoli, Paris",
      "status": "active"
    }
  }
}
```

`verified: false` significa que o nome não correspondeu — `restaurantDetails` é omitido, peça ao titular da conta que tente novamente. Limitado a 3 tentativas a cada 5 minutos (mais rigoroso que a verificação de existência, já que esta é a etapa de prova real). Um `restaurant_id` que não é mais resolvido no Zenchef retorna `404`.

**Etapa 3 — Salvar o restaurante**

`POST /appointments/zenchef-restaurants`

| Campo | Obrigatório | Descrição |
|---|---|---|
| `restaurant_id` | Sim | 1–64 caracteres, letras/números/sublinhado/hífen. |
| `restaurant_name` | Sim | O nome do restaurante verificado da etapa 2. |

```bash
curl -X POST "https://api.youraiconnector.com/v1/appointments/zenchef-restaurants?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "restaurant_id": "12345", "restaurant_name": "The Blue Door Bistro" }'
```

**Resposta** (`201 Created`):

```json
{ "success": true, "data": { "restaurantId": "12345" } }
```

**Atualizar um restaurante Zenchef salvo**

`PUT /appointments/zenchef-restaurants/{restaurantId}`

| Campo | Obrigatório | Descrição |
|---|---|---|
| `restaurant_name` | Não | Novo nome de exibição. |
| `is_active` | Não | Defina `false` para impedir que o bot faça reservas neste restaurante sem removê-lo. |

```bash
curl -X PUT "https://api.youraiconnector.com/v1/appointments/zenchef-restaurants/12345" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "is_active": false }'
```

**Resposta** (`200 OK`): mesmo formato da resposta de salvamento acima.

**Remover um restaurante Zenchef**

`DELETE /appointments/zenchef-restaurants/{restaurantId}`

```bash
curl -X DELETE "https://api.youraiconnector.com/v1/appointments/zenchef-restaurants/12345" \
  -H "X-API-Key: YOUR_API_KEY"
```

**Resposta** (`200 OK`): `{ "success": true, "data": { "restaurantId": "12345" } }`

Um `restaurantId` que não está atualmente na conta retorna `404` ao atualizar ou excluir.

### Formitable

O Formitable não precisa da prova de nome em duas etapas que o Zenchef exige — seus IDs de restaurante já são delimitados por empresa, portanto, uma chamada de verificação é suficiente. Ele também possui uma consulta de detalhes usada para armazenar em cache a URL do site do restaurante durante a configuração.

**Verificar um ID de restaurante**

`POST /appointments/formitable-restaurants/verify`

| Campo | Obrigatório | Descrição |
|---|---|---|
| `restaurant_id` | Sim | O ID do restaurante Formitable. |
| `language` | Não | Tag de idioma para a solicitação de teste. O padrão é `"nl"`. |

```bash
curl -X POST "https://api.youraiconnector.com/v1/appointments/formitable-restaurants/verify?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "restaurant_id": "the-blue-door", "language": "en" }'
```

**Resposta** (`200 OK`):

```json
{
  "success": true,
  "data": {
    "verified": true,
    "restaurantDetails": {
      "restaurantId": "the-blue-door",
      "productCount": 4,
      "sampleProductTitle": "Dinner for two",
      "language": "en"
    }
  }
}
```

Um `restaurant_id` que o Formitable não reconhece retorna `404`. Limitado a 10 tentativas a cada 5 minutos por conta.

**Obter detalhes do restaurante**

`GET /appointments/formitable-restaurants/{restaurantId}/details?language=en`

Busca o perfil público do restaurante no Formitable, incluindo seu site — usado para armazenar em cache a URL do site durante a configuração do restaurante. `language` é um parâmetro de consulta opcional, com padrão para `"en"`.

```bash
curl "https://api.youraiconnector.com/v1/appointments/formitable-restaurants/the-blue-door/details?language=en" \
  -H "X-API-Key: YOUR_API_KEY"
```

**Resposta** (`200 OK`):

```json
{
  "success": true,
  "data": {
    "uid": "the-blue-door",
    "name": "The Blue Door Bistro",
    "website": "https://thebluedoorbistro.com",
    "email": "info@thebluedoorbistro.com",
    "telephone": "+31201234567",
    "streetAddress": "Prinsengracht 1",
    "zipcode": "1015 AB",
    "city": "Amsterdam",
    "country": "Netherlands",
    "countryCode": "NL",
    "currency": "EUR"
  }
}
```

**Salvar o restaurante**

`POST /appointments/formitable-restaurants`

| Campo | Obrigatório | Descrição |
|---|---|---|
| `restaurant_id` | Sim | 1–64 caracteres, letras/números/sublinhado/hífen. |
| `restaurant_name` | Sim | Nome de exibição. |
| `language` | Sim | Tag de idioma ISO, ex: `"en"` ou `"en-GB"`. |
| `website_url` | Não | O site do restaurante, a partir da consulta de detalhes acima. Deve ser `http(s)://`. |

```bash
curl -X POST "https://api.youraiconnector.com/v1/appointments/formitable-restaurants?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "restaurant_id": "the-blue-door",
    "restaurant_name": "The Blue Door Bistro",
    "language": "en",
    "website_url": "https://thebluedoorbistro.com"
  }'
```

**Resposta** (`201 Created`): `{ "success": true, "data": { "restaurantId": "the-blue-door" } }`

**Atualizar um restaurante Formitable salvo**

`PUT /appointments/formitable-restaurants/{restaurantId}`

| Campo | Obrigatório | Descrição |
|---|---|---|
| `restaurant_name` | Não | Novo nome de exibição. |
| `language` | Não | Nova tag de idioma ISO. |
| `is_active` | Não | Defina `false` para impedir que o bot faça reservas neste restaurante sem removê-lo. |
| `website_url` | Não | Nova URL do site. |

```bash
curl -X PUT "https://api.youraiconnector.com/v1/appointments/formitable-restaurants/the-blue-door" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "is_active": false }'
```

**Resposta** (`200 OK`): mesmo formato da resposta de salvamento acima.

**Remover um restaurante Formitable**

`DELETE /appointments/formitable-restaurants/{restaurantId}`

```bash
curl -X DELETE "https://api.youraiconnector.com/v1/appointments/formitable-restaurants/the-blue-door" \
  -H "X-API-Key: YOUR_API_KEY"
```

**Resposta** (`200 OK`): `{ "success": true, "data": { "restaurantId": "the-blue-door" } }`

Um `restaurantId` que não está atualmente na conta retorna `404` ao atualizar ou excluir.

> **Formato de erro em todos os endpoints Zenchef/Formitable:** ao contrário do restante desta página, os erros aqui carregam seu status duas vezes — uma como o status HTTP e outra como `error_code` no corpo — por exemplo `{ "success": false, "error": "Restaurant not found", "error_code": 404 }`. Trate-o da mesma forma que qualquer outro erro: verifique `success`, leia `error` para a mensagem.

---

## Erros da API de Agendamentos

Os endpoints de agendamento retornam o envelope de erro padrão:

```json
{
  "success": false,
  "error": "Appointment not found"
}
```

| Status | Quando ocorre em um endpoint de agendamento |
|---|---|
| `400` | Um campo obrigatório está faltando ou é inválido — por exemplo, um `start_time` incorreto, um `end_time` que não é posterior a `start_time`, uma combinação de filtros inválida, nenhum campo para atualizar ou um agendamento já cancelado. |
| `404` | O agendamento, contato ou tipo de evento não foi encontrado. |
| `409` | O intervalo de tempo solicitado já está ocupado (conflito de agendamento). |

Os códigos compartilhados que todo endpoint pode retornar — `401`, `403` (seu plano não inclui acesso à API), `429` (limite de taxa) e `500` — estão listados com orientações de nova tentativa em [Erros e Paginação](errors-and-pagination.md).

---

## Próximos passos

- [Contatos](contacts.md) — crie e consulte os contatos para os quais você faz reservas.
- [Mensagens e Conversas](messages.md) — envie uma confirmação ou lembrete a um contato.
- [Webhooks](webhooks.md) — receba notificações quando agendamentos forem alterados.
