Skip to content

Agenda ​

A API lê os serviços e os horários livres e cria e gerencia os agendamentos da Agenda da empresa. Todo agendamento criado por aqui aparece no calendário da tela como qualquer outro, com a origem "API". As regras de horário (expediente, feriados, capacidade, antecedência mínima e limite de dias) são as mesmas da tela.

Para ser avisado quando algo muda na agenda, veja Webhooks. Para quem não programa, veja Integrações da agenda.

Antes de começar ​

  • O endereço base é https://konversia.com.br/api/v1/agenda, com o cabeçalho Authorization: Bearer <sua-chave>. Veja Autenticação.
  • Existem dois escopos: agenda:read (os quatro GET) e agenda:write (todo POST e o DELETE). Sem o escopo, a resposta é 403 forbidden_scope. Veja Permissões para o que mais cada um libera.
  • A Agenda é um módulo à parte. Com o módulo desligado na empresa, todo endpoint desta página devolve 403 module_disabled, com error.details.module igual a agenda.
  • Um id (de agendamento, serviço ou contato) de outra empresa ou inexistente devolve 404 not_found. A exceção são os filtros service_id e contact_id da listagem de agendamentos: ali o id só não encontra nada, e a resposta é 200 com data vazio. Um id malformado (abc, por exemplo) só devolve 404 not_found quando é o id do agendamento na URL. Um service_id ou contact_id malformado no corpo ou na consulta devolve 422 validation_failed.
  • O limite é de 120 requisições por minuto por chave, o mesmo da API toda.
Método e caminhoEscopoO que faz
GET /v1/agenda/servicesagenda:readLista os serviços ativos
GET /v1/agenda/availabilityagenda:readHorários livres de um serviço
GET /v1/agenda/appointmentsagenda:readLista agendamentos, com filtros
GET /v1/agenda/appointments/{id}agenda:readUm agendamento
POST /v1/agenda/appointmentsagenda:writeCria um agendamento
POST /v1/agenda/appointments/{id}/rescheduleagenda:writeRemarca
POST /v1/agenda/appointments/{id}/cancelagenda:writeCancela
POST /v1/agenda/appointments/{id}/confirmagenda:writeMarca como confirmado
POST /v1/agenda/appointments/{id}/completeagenda:writeMarca como concluído
POST /v1/agenda/appointments/{id}/no-showagenda:writeMarca como não compareceu
POST /v1/agenda/appointments/{id}/paymentagenda:writeRegistra o pagamento
DELETE /v1/agenda/appointments/{id}/paymentagenda:writeDesfaz o registro de pagamento

Objeto agendamento ​

Todo endpoint que devolve um agendamento (detalhe, criar, remarcar, cancelar, confirmar, concluir, falta e pagamento), cada item da lista e os webhooks appointment.* usam exatamente este formato. Campos novos podem ser acrescentados no futuro; nenhum é renomeado ou removido.

json
{
  "id": 128,
  "status": "scheduled",
  "starts_at": "2026-10-06T17:00:00+00:00",
  "ends_at": "2026-10-06T18:00:00+00:00",
  "timezone": "America/Sao_Paulo",
  "date": "2026-10-06",
  "start": "14:00",
  "end": "15:00",
  "source": "api",
  "notes": "Primeira vez",
  "price_cents": 5000,
  "payment": { "status": "pending", "method": null, "paid_at": null },
  "confirmed_at": null,
  "canceled_at": null,
  "canceled_by": null,
  "cancel_reason": null,
  "reservation_url": "https://konversia.com.br/reserva/Xk3pQ9vL2mZr7TbN4wYc8HsD1fGj6AeU5oRiKtVy",
  "service": { "id": 3, "name": "Corte", "duration_minutes": 60 },
  "contact": { "id": 331, "name": "Maria Souza", "phone_number": "5511977770000" },
  "conversation_id": 918,
  "responsible": null,
  "created_at": "2026-10-05T10:00:00+00:00",
  "updated_at": "2026-10-05T10:00:00+00:00"
}
CampoTipoDescrição
idintegerIdentificador do agendamento
statusstringscheduled (agendado), confirmed (confirmado), completed (concluído), no_show (não compareceu) ou canceled (cancelado)
starts_atstringInício, ISO 8601 em UTC
ends_atstringFim, ISO 8601 em UTC
timezonestringFuso da empresa, por exemplo America/Sao_Paulo
datestringDia do atendimento no fuso da empresa, AAAA-MM-DD
startstringHora de início no fuso da empresa, HH:MM
endstringHora de fim no fuso da empresa, HH:MM
sourcestringComo o agendamento nasceu: manual (equipe, pela tela), workflow, public_link (link público) ou api
notesstring ou nullObservação interna
price_centsinteger ou nullValor em centavos
payment.statusstringpending ou paid
payment.methodstring ou nullpix, credit_card, debit_card, cash, bank_transfer ou other
payment.paid_atstring ou nullQuando foi pago, ISO 8601 em UTC
confirmed_atstring ou nullQuando foi confirmado
canceled_atstring ou nullQuando foi cancelado
canceled_bystring ou nullteam (a empresa) ou contact (o cliente)
cancel_reasonstring ou nullMotivo informado no cancelamento
reservation_urlstring ou nullPágina da reserva. Quem tem este endereço vê, remarca e cancela o agendamento, sem login: trate como segredo. Só vem preenchido para chave com agenda:write; com só agenda:read vem null. Veja Permissões
serviceobjectid, name e duration_minutes do serviço
contactobjectid, name e phone_number do cliente
conversation_idinteger ou nullConversa de WhatsApp ligada ao agendamento. null quando o contato ainda não tem conversa
responsibleobject ou nullid e name do responsável pelo atendimento
created_at, updated_atstringCriação e última alteração, ISO 8601 em UTC

Permissões ​

ParaA chave precisa de
Ler serviços, horários livres e agendamentosagenda:read
Receber o reservation_url preenchidoagenda:write
Criar, remarcar, cancelar, confirmar, concluir, marcar falta e registrar pagamentoagenda:write
Avisar o cliente na hora, com notify_contact: true em criar ou remarcaragenda:write e messages:send
  • O endereço da reserva é poder de escrita. A página da reserva deixa quem a abre remarcar e cancelar o agendamento, sem login. Por isso uma chave só com agenda:read recebe o campo reservation_url com null, na lista e no detalhe. O campo continua existindo no objeto.
  • Guarde o reservation_url como guarda uma senha. Envie só para o próprio cliente. Não coloque em planilha compartilhada, em evento de calendário que outras pessoas veem, nem em registro de log.
  • notify_contact: true é envio de mensagem. A linha da empresa manda uma mensagem para o telefone informado, então a chave precisa também de messages:send. Sem ele, a resposta é 403 forbidden_scope, com error.details.required_scope igual a messages:send, e nada é criado nem remarcado.
  • Lembretes não dependem da chave. Um agendamento criado pela API recebe os lembretes e a mensagem após o atendimento configurados na Agenda, como qualquer outro agendamento da empresa, mesmo que a chave não tenha messages:send. Quem decide essas mensagens é a configuração da Agenda, não o integrador.

Datas e fusos ​

  • Entrada (starts_at em criar e remarcar, paid_at no pagamento): ISO 8601 com fuso obrigatório. São aceitos 2026-10-06T14:00:00-03:00, 2026-10-06T14:00-03:00, 2026-10-06T17:00:00Z e as variantes com milissegundos (2026-10-06T17:00:00.000Z, o formato do toISOString() do JavaScript). Sem fuso (2026-10-06T14:00:00, 2026-10-06 14:00) é recusado com validation_failed em starts_at.
  • A hora vai de 00 a 23 e os minutos e segundos de 00 a 59. T24:00 e :60 são recusados com validation_failed.
  • Qualquer fuso serve: o instante é convertido. Segundos são ignorados, porque a agenda trabalha em minutos.
  • Saída: starts_at e ends_at vêm sempre em UTC (+00:00). timezone, date, start e end trazem o horário local da empresa pronto para mostrar, sem conta de fuso do seu lado.
  • Os parâmetros de dia (from, to) são dias do calendário da empresa, no formato AAAA-MM-DD.

Listar serviços ​

Devolve só os serviços ativos, na ordem definida na tela, sem paginação. public_bookable diz se o serviço aparece no link público de agendamento. min_notice_minutes é a antecedência mínima e max_days_ahead o limite de dias para agendar; os dois já estão aplicados nos horários livres.

bash
curl https://konversia.com.br/api/v1/agenda/services \
  -H "Authorization: Bearer $KONVERSIA_API_KEY"
json
{
  "data": [
    {
      "id": 3,
      "name": "Corte",
      "description": "Corte simples",
      "duration_minutes": 60,
      "price_cents": 5000,
      "min_notice_minutes": 60,
      "max_days_ahead": 30,
      "public_bookable": true
    }
  ]
}

Consultar horários livres ​

Parâmetros, na query string:

ParâmetroTipoObrigatórioDescrição
service_idintegersimServiço consultado
fromstringnãoPrimeiro dia, AAAA-MM-DD. Padrão: hoje
tostringnãoÚltimo dia, AAAA-MM-DD. Padrão: 14 dias depois de from
  • A janela máxima é de 31 dias: to pode ser até 31 dias depois de from. Passou disso, ou to antes de from: 422 validation_failed, com o motivo em error.details.to. Para olhar mais longe, faça mais de uma chamada.
  • days só traz os dias com pelo menos um horário livre. Dia fechado, feriado ou lotado não aparece.
  • Cada horário traz start e end locais, starts_at em UTC (é o valor a enviar em criar e remarcar) e remaining, quantos agendamentos ainda cabem naquele horário.
  • Serviço inativo, apagado ou de outra empresa: 404 not_found.
  • Horário livre não é reserva: outro agendamento pode ocupar o horário entre a consulta e a criação, e aí a criação devolve 409 slot_unavailable.
bash
curl "https://konversia.com.br/api/v1/agenda/availability?service_id=3&from=2026-10-06&to=2026-10-07" \
  -H "Authorization: Bearer $KONVERSIA_API_KEY"
json
{
  "timezone": "America/Sao_Paulo",
  "days": [
    {
      "date": "2026-10-06",
      "slots": [
        { "start": "08:00", "end": "09:00", "starts_at": "2026-10-06T11:00:00+00:00", "remaining": 1 },
        { "start": "14:00", "end": "15:00", "starts_at": "2026-10-06T17:00:00+00:00", "remaining": 1 }
      ]
    },
    {
      "date": "2026-10-07",
      "slots": [
        { "start": "10:00", "end": "11:00", "starts_at": "2026-10-07T13:00:00+00:00", "remaining": 2 }
      ]
    }
  ]
}

Listar agendamentos ​

Todos os filtros são opcionais e podem ser combinados:

FiltroDescrição
from, toAAAA-MM-DD, pelo dia local do atendimento, os dois inclusivos
statusUm dos cinco valores de status
service_idServiço
contact_idContato
phoneTelefone do cliente, só dígitos, com ou sem +
sourcemanual, workflow, public_link ou api
per_pageDe 1 a 100. Padrão 25
pagePágina. Padrão 1
  • A ordem é do mais cedo para o mais tarde (starts_at).
  • A resposta traz data (a lista de agendamentos) e meta (current_page, per_page, last_page, total).
  • per_page fora da faixa não é erro: é ajustado para o limite mais próximo. Uma page inválida cai na primeira.
  • Filtro com valor inválido (status desconhecido, data em outro formato): 422 validation_failed.
  • Sem from e to, vem tudo, inclusive o passado e os cancelados. Filtre.
bash
curl "https://konversia.com.br/api/v1/agenda/appointments?from=2026-10-06&to=2026-10-06&status=scheduled&per_page=50" \
  -H "Authorization: Bearer $KONVERSIA_API_KEY"
json
{
  "data": [
    { "id": 128, "status": "scheduled", "starts_at": "2026-10-06T17:00:00+00:00", "...": "os demais campos do objeto agendamento" }
  ],
  "meta": { "current_page": 1, "per_page": 50, "last_page": 1, "total": 1 }
}

Cada item traz o objeto agendamento completo; o exemplo está abreviado.

Ver um agendamento ​

Devolve o objeto agendamento direto, sem envelope data. Um id de outra empresa ou inexistente devolve 404 not_found.

bash
curl https://konversia.com.br/api/v1/agenda/appointments/128 \
  -H "Authorization: Bearer $KONVERSIA_API_KEY"

Criar um agendamento ​

CampoTipoObrigatórioDescrição
service_idintegersimServiço ativo da empresa
starts_atstringsimInício, ISO 8601 com fuso. Use um starts_at devolvido em Consultar horários livres
contact_idintegerum dos doisContato que já existe na empresa
tostringum dos doisTelefone do cliente, com ou sem + e DDI. O contato e a conversa são criados se ainda não existirem
contact_namestringnãoNome do cliente, usado só quando o contato é criado agora
channel_idintegernãoLinha de WhatsApp do contato, só com to. Mesma regra de Para onde a mensagem vai
notesstringnãoObservação interna, até 2000 caracteres
responsible_user_idintegernãoUsuário ativo da empresa responsável pelo atendimento
notify_contactbooleannãoEnvia ao cliente a mensagem "Ao agendar" da Agenda. Com true, a chave precisa também do escopo messages:send. Padrão false
  • O cabeçalho Idempotency-Key é obrigatório (ausente: 422 validation_failed). A resposta é 201 com o objeto agendamento, com source igual a api.
  • Informe o cliente por contact_id ou por to. Os dois juntos: 422 validation_failed. Nenhum dos dois: 422 contact_required.
  • Com to, o canal precisa estar conectado e ter setor padrão. Os erros são channel_not_found, channel_disconnected, channel_without_default_department, invalid_phone_number e contact_blocked, os mesmos do envio de mensagens. Se o contato não existe, ele e a conversa só são criados quando o horário é aceito: um pedido recusado não deixa contato nem conversa para trás. A conversa nova nasce como não iniciada, então não entra na fila de pendentes. Uma conversa que já existe não é alterada: status, setor e atendente ficam como estão.
  • Com contact_id, o agendamento usa a conversa de WhatsApp mais recente daquele contato. Se ele não tem nenhuma, conversation_id vem null e as mensagens automáticas da Agenda não têm por onde sair (aparecem na tela como não enviadas, com o motivo "Contato sem conversa"). Para um cliente que nunca conversou com a empresa, prefira to.
  • Horário ocupado, fora do expediente, em feriado, antes da antecedência mínima, além do limite de dias ou no passado: 409 slot_unavailable. Consulte os horários livres de novo.
  • 409 agenda_busy quer dizer que a agenda estava ocupada naquele instante e nada foi decidido sobre o horário. Repita a mesma chamada, com a mesma Idempotency-Key, depois de um ou dois segundos.
  • Um responsible_user_id que não é um usuário ativo da empresa: 422 validation_failed em responsible_user_id.
  • Com notify_contact: true, sai a mensagem Ao agendar configurada nas mensagens da Agenda, se ela estiver ativa. Isso exige que a chave tenha também messages:send: sem ele, 403 forbidden_scope e nenhum agendamento é criado. Lembretes e mensagens após o atendimento seguem as regras da Agenda nos dois casos, com ou sem notify_contact, e não dependem dos escopos da chave. Essas mensagens contam para o limite mensal de envios da empresa.
  • Workflows com o disparo Evento da agenda, evento Agendamento criado, origem Qualquer ou Pela API, começam para este agendamento, desde que ele tenha uma conversa de WhatsApp e não haja outro workflow em andamento nela.
bash
curl -X POST https://konversia.com.br/api/v1/agenda/appointments \
  -H "Authorization: Bearer $KONVERSIA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: pedido-8842" \
  -d '{
    "service_id": 3,
    "starts_at": "2026-10-06T14:00:00-03:00",
    "to": "5511977770000",
    "contact_name": "Maria Souza",
    "notes": "Primeira vez",
    "notify_contact": true
  }'

A resposta 201 é o objeto agendamento do exemplo de Objeto agendamento. Por contato que já existe:

bash
curl -X POST https://konversia.com.br/api/v1/agenda/appointments \
  -H "Authorization: Bearer $KONVERSIA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: pedido-8843" \
  -d '{ "service_id": 3, "starts_at": "2026-10-07T13:00:00Z", "contact_id": 331 }'

Exemplo de recusa:

json
{
  "error": {
    "code": "slot_unavailable",
    "message": "Esse horário não está disponível: ocupado, fora do expediente, antes da antecedência mínima ou além do limite de dias do serviço. Consulte os horários livres.",
    "details": {},
    "docs_url": "https://docs.konversia.com.br/api/erros/slot_unavailable"
  }
}

Idempotência ​

  • A regra é a mesma do envio de mensagens (veja Cabeçalho obrigatório: Idempotency-Key). Escolha um valor único por pedido de negócio, como o id da reserva no seu sistema, e não um valor sorteado a cada tentativa.
  • Mesma chave e mesmo corpo: devolve a resposta da primeira chamada, com 201 de novo, sem criar outro agendamento. Essa resposta é o retrato de quando o agendamento foi criado. Para o estado atual, use GET /v1/agenda/appointments/{id}.
  • Mesma chave e corpo diferente: 409 idempotency_key_reused.
  • Duas chamadas simultâneas com a mesma chave: a segunda espera a primeira terminar e devolve a mesma resposta. Só se essa espera estourar o tempo ela recebe 409 request_in_progress; aí basta repetir.
  • Um pedido recusado (slot_unavailable, validation_failed e os demais erros) não gasta a chave: você pode repetir com a mesma chave depois de corrigir a causa.
  • A chave vale por 24 horas.
  • Os demais endpoints desta página não usam Idempotency-Key.

Remarcar ​

CampoTipoObrigatórioDescrição
starts_atstringsimNovo início, ISO 8601 com fuso
notify_contactbooleannãoAvisa o cliente do novo horário com a mensagem da Agenda. Com true, a chave precisa também do escopo messages:send. Padrão false
  • Só vale para agendamento scheduled ou confirmed. Concluído, com falta ou cancelado: 422 invalid_appointment_state.
  • O agendamento volta para scheduled (uma confirmação anterior deixa de valer) e os lembretes são reprogramados para o novo horário.
  • Remarcar para o mesmo instante que o agendamento já tem devolve 200 com o estado atual e não muda nada: a confirmação continua valendo e o cliente não é avisado.
  • Horário novo indisponível: 409 slot_unavailable, e o agendamento fica como estava.
  • notify_contact: true com uma chave sem messages:send: 403 forbidden_scope, e o agendamento fica como estava.
  • A resposta é 200 com o objeto agendamento.
bash
curl -X POST https://konversia.com.br/api/v1/agenda/appointments/128/reschedule \
  -H "Authorization: Bearer $KONVERSIA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "starts_at": "2026-10-07T10:00:00-03:00", "notify_contact": true }'

Cancelar ​

O corpo é opcional: reason (string, até 255 caracteres) e canceled_by (team, o padrão, ou contact).

  • Cancelar libera o horário e interrompe os lembretes pendentes. Não tem como desfazer: para voltar, crie outro agendamento.
  • Use canceled_by: "contact" quando o seu sistema age em nome do cliente (ele cancelou no seu site, por exemplo). A equipe é avisada, como em qualquer cancelamento feito pelo cliente, e os workflows com o evento Cliente cancelou começam. Com team, ninguém é avisado.
  • Repetir a chamada num agendamento já cancelado devolve 200 com o estado atual, sem erro e sem alterar quem cancelou nem o motivo. É seguro repetir, inclusive com duas chamadas ao mesmo tempo.
  • Agendamento concluído ou com falta: 422 invalid_appointment_state.
bash
curl -X POST https://konversia.com.br/api/v1/agenda/appointments/128/cancel \
  -H "Authorization: Bearer $KONVERSIA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "reason": "Cliente pediu pelo site", "canceled_by": "contact" }'

Trecho da resposta:

json
{
  "id": 128,
  "status": "canceled",
  "canceled_at": "2026-10-05T10:00:00+00:00",
  "canceled_by": "contact",
  "cancel_reason": "Cliente pediu pelo site"
}

Confirmar, concluir e marcar falta ​

Os três endpoints não têm corpo e respondem 200 com o objeto agendamento.

EndpointVale paraResultado
POST /v1/agenda/appointments/{id}/confirmscheduledconfirmed, com confirmed_at preenchido
POST /v1/agenda/appointments/{id}/completescheduled, confirmed ou no_showcompleted
POST /v1/agenda/appointments/{id}/no-showscheduled, confirmed ou completedno_show
  • Fora dessas situações, a resposta é 422 invalid_appointment_state. Confirmar um agendamento já confirmado também cai aqui: leia o status antes, ou trate esse erro como "já estava feito".
  • Confirmar pela API avisa a equipe de que o cliente confirmou, igual a quando o cliente confirma pelo lembrete.
  • complete e no-show corrigem um ao outro: um agendamento marcado como falta por engano pode ser concluído, e vice-versa.
  • A mensagem após o atendimento da Agenda só sai para agendamento concluído.
bash
curl -X POST https://konversia.com.br/api/v1/agenda/appointments/128/confirm \
  -H "Authorization: Bearer $KONVERSIA_API_KEY"

Pagamento ​

Corpo do POST .../payment:

CampoTipoObrigatórioDescrição
methodstringsimpix, credit_card, debit_card, cash, bank_transfer ou other
paid_atstringnãoQuando foi pago, ISO 8601 com fuso. Padrão: agora
amount_centsintegernãoValor em centavos, maior ou igual a zero. Quando vem, passa a ser o price_cents do agendamento
  • É só um registro para o resumo financeiro da Agenda: nenhuma cobrança é feita.
  • O registro não muda o status do agendamento.
  • Agendamento cancelado: 422 invalid_appointment_state.
  • DELETE .../payment volta payment para pending (método e data null) e mantém o price_cents.
  • Os dois respondem 200 com o objeto agendamento.
  • Registrar ou desfazer pagamento não gera webhook.
bash
curl -X POST https://konversia.com.br/api/v1/agenda/appointments/128/payment \
  -H "Authorization: Bearer $KONVERSIA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "method": "pix", "paid_at": "2026-10-06T15:05:00-03:00", "amount_cents": 5000 }'
json
{
  "id": 128,
  "status": "completed",
  "price_cents": 5000,
  "payment": { "status": "paid", "method": "pix", "paid_at": "2026-10-06T18:05:00+00:00" }
}
bash
curl -X DELETE https://konversia.com.br/api/v1/agenda/appointments/128/payment \
  -H "Authorization: Bearer $KONVERSIA_API_KEY"

Erros ​

Todo erro segue o envelope descrito em Erros. Os códigos que esta página pode devolver:

CódigoStatus HTTPQuando
module_disabled403O módulo Agenda não está ativo na empresa (error.details.module)
forbidden_scope403A chave não tem agenda:read ou agenda:write, ou pediu notify_contact: true sem messages:send (error.details.required_scope diz qual falta)
not_found404Agendamento, serviço ou contato inexistente, de outra empresa, ou serviço inativo
slot_unavailable409Horário ocupado, fora do expediente, antes da antecedência mínima ou além do limite de dias
agenda_busy409A agenda estava ocupada naquele instante. Repita a chamada
idempotency_key_reused409Mesma Idempotency-Key com corpo diferente
request_in_progress409Outra chamada com a mesma Idempotency-Key ainda está em andamento e a espera por ela estourou o tempo
invalid_appointment_state422A situação atual do agendamento não permite a ação
contact_required422Faltou informar o cliente: contact_id ou to
validation_failed422Campo ausente ou inválido (error.details diz qual), starts_at sem fuso ou com hora impossível, service_id ou contact_id malformado, janela maior que 31 dias, Idempotency-Key ausente
invalid_phone_number422to não é um telefone reconhecível
contact_blocked422O contato do telefone informado está bloqueado
channel_not_found422Não foi possível escolher o canal (error.details.available_channels)
channel_without_default_department422O canal não tem setor padrão
channel_disconnected400O canal está desconectado
rate_limited429Limite de chamadas por minuto atingido

Passo a passo: consultar horários e agendar ​

  1. Liste os serviços e guarde o id do que o cliente quer (aqui, 3).

    bash
    curl https://konversia.com.br/api/v1/agenda/services \
      -H "Authorization: Bearer $KONVERSIA_API_KEY"
  2. Consulte os horários livres da semana, mostre ao cliente o date e o start de cada horário e guarde o starts_at do escolhido.

    bash
    curl "https://konversia.com.br/api/v1/agenda/availability?service_id=3&from=2026-10-06&to=2026-10-12" \
      -H "Authorization: Bearer $KONVERSIA_API_KEY"
  3. Crie o agendamento com o starts_at guardado e use o id da reserva no seu sistema como Idempotency-Key. Guarde o id devolvido. O exemplo avisa o cliente na hora (notify_contact), então a chave precisa de agenda:write e messages:send.

    bash
    curl -X POST https://konversia.com.br/api/v1/agenda/appointments \
      -H "Authorization: Bearer $KONVERSIA_API_KEY" \
      -H "Content-Type: application/json" \
      -H "Idempotency-Key: pedido-8842" \
      -d '{
        "service_id": 3,
        "starts_at": "2026-10-06T17:00:00+00:00",
        "to": "5511977770000",
        "contact_name": "Maria Souza",
        "notify_contact": true
      }'
  4. Se vier 409 slot_unavailable, alguém ocupou o horário entre o passo 2 e o 3: volte ao passo 2 e ofereça outro horário. Se vier 409 agenda_busy, repita o passo 3 com a mesma chave.

Para acompanhar o que acontece depois (o cliente confirmou pelo lembrete, cancelou pela página da reserva, a equipe remarcou na tela), cadastre um webhook com os eventos appointment.* em vez de consultar de tempos em tempos.

Relacionado ​