DEV Community

Cover image for Como Usar APIs Calendly: Guia do Desenvolvedor para Integração de Agendamentos
Lucas
Lucas

Posted on • Originally published at apidog.com

Como Usar APIs Calendly: Guia do Desenvolvedor para Integração de Agendamentos

Resumo

As APIs do Calendly permitem automatizar fluxos de trabalho de agendamento. Você se autentica com OAuth 2.0, acessa tipos de eventos e agendamentos via api.calendly.com, e recebe atualizações em tempo real via webhooks. Para testar integrações, utilize o Apidog para validar payloads de webhook sem precisar criar agendamentos reais.

Experimente o Apidog hoje

Introdução

O Calendly processa milhões de reuniões por mês e é amplamente usado para chamadas de vendas, sessões de suporte, consultorias e entrevistas. A API permite incorporar funcionalidades de agendamento diretamente em seus aplicativos.

O fluxo mais comum: agendamentos do Calendly disparam ações no seu sistema — como atualizar um CRM após uma demonstração agendada, enviar questionários antes de consultorias, ou notificar equipes sobre cancelamentos.

A API do Calendly gerencia isso via webhooks: sempre que eventos ocorrem (criação, cancelamento, reagendamento), o Calendly envia POSTs para seus endpoints. Seu sistema processa o payload e executa as ações necessárias.

💡 Dica: Se está integrando agendamento, o Apidog facilita testar manipuladores de webhook e validar payloads. Simule respostas do Calendly e garanta que sua integração cubra todos os eventos antes de conectar a calendários reais.

Autenticação com OAuth 2.0

O Calendly utiliza OAuth 2.0 para acesso à API. Não é possível autenticar apenas com chave de API.

Criar um aplicativo OAuth

  1. Acesse Calendly → Integrações → API & Webhooks
  2. Clique em “Criar Novo Aplicativo”
  3. Configure sua URI de redirecionamento (ex.: https://yourapp.com/auth/calendly/callback)
  4. Salve o client ID e client secret

O fluxo OAuth

Passo 1: Redirecione o usuário para autorização

https://auth.calendly.com/oauth/authorize?
  client_id=YOUR_CLIENT_ID&
  response_type=code&
  redirect_uri=https://yourapp.com/auth/calendly/callback
Enter fullscreen mode Exit fullscreen mode

Passo 2: Usuário autoriza e retorna para seu callback

https://yourapp.com/auth/calendly/callback?code=AUTHORIZATION_CODE
Enter fullscreen mode Exit fullscreen mode

Passo 3: Troque o código por um token de acesso

const response = await fetch('https://auth.calendly.com/oauth/token', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/x-www-form-urlencoded',
    'Authorization': 'Basic ' + Buffer.from(clientId + ':' + clientSecret).toString('base64')
  },
  body: new URLSearchParams({
    grant_type: 'authorization_code',
    code: authCode,
    redirect_uri: 'https://yourapp.com/auth/calendly/callback'
  })
})

const { access_token, refresh_token, expires_in } = await response.json()
Enter fullscreen mode Exit fullscreen mode

Passo 4: Use o token para acessar a API

curl -X GET "https://api.calendly.com/users/me" \
  -H "Authorization: Bearer ACCESS_TOKEN"
Enter fullscreen mode Exit fullscreen mode

Tokens de atualização

Tokens de acesso expiram em 2 horas. Use o refresh token para obter um novo:

const response = await fetch('https://auth.calendly.com/oauth/token', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/x-www-form-urlencoded',
    'Authorization': 'Basic ' + Buffer.from(clientId + ':' + clientSecret).toString('base64')
  },
  body: new URLSearchParams({
    grant_type: 'refresh_token',
    refresh_token: storedRefreshToken
  })
})
Enter fullscreen mode Exit fullscreen mode

Obtendo informações do usuário

Obter usuário atual

curl -X GET "https://api.calendly.com/users/me" \
  -H "Authorization: Bearer ACCESS_TOKEN"
Enter fullscreen mode Exit fullscreen mode

Resposta:

{
  "resource": {
    "avatar_url": "https://calendly.com/avatar.jpg",
    "created_at": "2024-01-15T10:00:00Z",
    "current_organization": "https://api.calendly.com/organizations/ABC123",
    "email": "you@example.com",
    "name": "John Doe",
    "scheduling_url": "https://calendly.com/johndoe",
    "slug": "johndoe",
    "timezone": "America/New_York",
    "uri": "https://api.calendly.com/users/ABC123"
  }
}
Enter fullscreen mode Exit fullscreen mode

Obter associação à organização

curl -X GET "https://api.calendly.com/organization_memberships/me" \
  -H "Authorization: Bearer ACCESS_TOKEN"
Enter fullscreen mode Exit fullscreen mode

Tipos de evento

Tipos de evento são modelos de reunião criados (ex: chamada 30 min, consultoria 60 min, etc).

Listar tipos de evento

curl -X GET "https://api.calendly.com/event_types?user=https://api.calendly.com/users/ABC123" \
  -H "Authorization: Bearer ACCESS_TOKEN"
Enter fullscreen mode Exit fullscreen mode

Resposta:

{
  "resource": {
    "uri": "https://api.calendly.com/event_types/ETC123",
    "active": true,
    "booking_method": "instant",
    "color": "#0066FF",
    "created_at": "2024-01-15T10:00:00Z",
    "description_html": "<p>30-minute consultation</p>",
    "duration": 30,
    "internal_note": "Use Zoom link",
    "kind": "solo",
    "name": "30 Min Consultation",
    "pooling_type": null,
    "profile": {
      "name": "John Doe",
      "type": "User",
      "owner": "https://api.calendly.com/users/ABC123"
    },
    "scheduling_url": "https://calendly.com/johndoe/30min",
    "slug": "30min",
    "type": "StandardEventType"
  },
  "pagination": {
    "count": 1,
    "next_page": null
  }
}
Enter fullscreen mode Exit fullscreen mode

Obter um tipo de evento específico

curl -X GET "https://api.calendly.com/event_types/ETC123" \
  -H "Authorization: Bearer ACCESS_TOKEN"
Enter fullscreen mode Exit fullscreen mode

Eventos agendados (agendamentos)

Eventos são os agendamentos reais feitos via Calendly.

Listar eventos agendados

curl -X GET "https://api.calendly.com/scheduled_events?user=https://api.calendly.com/users/ABC123" \
  -H "Authorization: Bearer ACCESS_TOKEN"
Enter fullscreen mode Exit fullscreen mode

Filtrar por datas:

curl -X GET "https://api.calendly.com/scheduled_events?min_start_time=2026-03-01T00:00:00Z&max_start_time=2026-03-31T23:59:59Z" \
  -H "Authorization: Bearer ACCESS_TOKEN"
Enter fullscreen mode Exit fullscreen mode

Resposta:

{
  "resource": {
    "uri": "https://api.calendly.com/scheduled_events/ABC123",
    "status": "active",
    "tracking": {
      "utm_campaign": "spring_sale",
      "utm_source": "email",
      "utm_medium": "newsletter"
    },
    "created_at": "2026-03-24T10:00:00Z",
    "end_time": "2026-03-25T11:00:00Z",
    "event_type": "https://api.calendly.com/event_types/ETC123",
    "invitees_counter": {
      "active": 1,
      "limit": 1,
      "total": 1
    },
    "location": {
      "type": "zoom",
      "join_url": "https://zoom.us/j/123456789"
    },
    "start_time": "2026-03-25T10:30:00Z",
    "updated_at": "2026-03-24T10:00:00Z"
  }
}
Enter fullscreen mode Exit fullscreen mode

Obter detalhes do evento

curl -X GET "https://api.calendly.com/scheduled_events/EVENT_ID" \
  -H "Authorization: Bearer ACCESS_TOKEN"
Enter fullscreen mode Exit fullscreen mode

Obter convidados para um evento

curl -X GET "https://api.calendly.com/scheduled_events/EVENT_ID/invitees" \
  -H "Authorization: Bearer ACCESS_TOKEN"
Enter fullscreen mode Exit fullscreen mode

Resposta:

{
  "resource": [
    {
      "cancel_url": "https://calendly.com/cancellations/ABC123",
      "created_at": "2026-03-24T10:00:00Z",
      "email": "jane@example.com",
      "event": "https://api.calendly.com/scheduled_events/ABC123",
      "name": "Jane Smith",
      "new_invitee": null,
      "old_invitee": null,
      "reschedule_url": "https://calendly.com/reschedulings/ABC123",
      "status": "active",
      "text_reminder_number": "+15551234567",
      "timezone": "America/New_York",
      "tracking": {
        "utm_campaign": null,
        "utm_source": null
      },
      "updated_at": "2026-03-24T10:00:00Z",
      "uri": "https://api.calendly.com/scheduled_event_invitees/INV123",
      "canceled": null
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

Webhooks para atualizações em tempo real

Webhooks notificam seu app sobre eventos de agendamento em tempo real.

Criar uma assinatura de webhook

curl -X POST "https://api.calendly.com/webhook_subscriptions" \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://yourapp.com/webhooks/calendly",
    "events": [
      "invitee.created",
      "invitee.canceled",
      "invitee.rescheduled"
    ],
    "organization": "https://api.calendly.com/organizations/ORG123",
    "scope": "organization"
  }'
Enter fullscreen mode Exit fullscreen mode

Eventos disponíveis:

  • invitee.created — agendamento criado
  • invitee.canceled — agendamento cancelado
  • invitee.rescheduled — agendamento reagendado

Listar assinaturas de webhook

curl -X GET "https://api.calendly.com/webhook_subscriptions?organization=https://api.calendly.com/organizations/ORG123" \
  -H "Authorization: Bearer ACCESS_TOKEN"
Enter fullscreen mode Exit fullscreen mode

Excluir um webhook

curl -X DELETE "https://api.calendly.com/webhook_subscriptions/WEBHOOK_ID" \
  -H "Authorization: Bearer ACCESS_TOKEN"
Enter fullscreen mode Exit fullscreen mode

Manipulando payloads de webhook

Verificar assinaturas de webhook

O Calendly assina webhooks no cabeçalho Calendly-Webhook-Signature. Exemplo de validação em Node.js:

import crypto from 'crypto'

function verifySignature(payload, signature, secret) {
  const [t, v1] = signature.split(',')
  const timestamp = t.split('=')[1]
  const hash = v1.split('=')[1]

  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(timestamp + '.' + payload)
    .digest('hex')

  return crypto.timingSafeEqual(
    Buffer.from(hash),
    Buffer.from(expectedSignature)
  )
}

app.post('/webhooks/calendly', (req, res) => {
  const signature = req.headers['calendly-webhook-signature']
  const payload = JSON.stringify(req.body)

  if (!verifySignature(payload, signature, process.env.CALENDLY_WEBHOOK_SECRET)) {
    return res.status(401).send('Invalid signature')
  }

  // Processar webhook
  handleWebhook(req.body)
  res.status(200).send('OK')
})
Enter fullscreen mode Exit fullscreen mode

Processar eventos de agendamento

function handleWebhook(payload) {
  const { event, payload: data } = payload

  switch (event) {
    case 'invitee.created':
      console.log(`Novo agendamento: ${data.event.start_time}`)
      console.log(`Convidado: ${data.email}`)
      // Adicione ao CRM, envie e-mail de confirmação, etc.
      syncToCRM(data)
      break

    case 'invitee.canceled':
      console.log(`Agendamento cancelado: ${data.event.uri}`)
      // Atualize CRM, notifique equipe, etc.
      removeFromCRM(data)
      break

    case 'invitee.rescheduled':
      console.log(`Agendamento reagendado: ${data.event.start_time}`)
      // Atualize calendário, notifique equipe, etc.
      updateCRM(data)
      break
  }
}
Enter fullscreen mode Exit fullscreen mode

Testando com Apidog

A API do Calendly exige OAuth, dificultando testes rápidos. O Apidog simplifica esse processo.

image-237.png

1. Simular respostas OAuth

Durante o desenvolvimento, simule a resposta do token para não executar o fluxo OAuth sempre:

{
  "access_token": "mock_access_token",
  "refresh_token": "mock_refresh_token",
  "expires_in": 7200,
  "created_at": 1700000000
}
Enter fullscreen mode Exit fullscreen mode

2. Testar manipuladores de webhook

Monte payloads simulados de webhook:

{
  "created_at": "2026-03-24T10:00:00Z",
  "event": "invitee.created",
  "payload": {
    "email": "test@example.com",
    "name": "Test User",
    "event": {
      "start_time": "2026-03-25T10:30:00Z",
      "end_time": "2026-03-25T11:00:00Z",
      "event_type": {
        "name": "30 Min Consultation"
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Envie para seu endpoint e verifique o processamento.

3. Variáveis de ambiente

Utilize variáveis de ambiente para facilitar os testes:

CALENDLY_CLIENT_ID: abc123
CALENDLY_CLIENT_SECRET: xyz789
CALENDLY_ACCESS_TOKEN: stored_token
CALENDLY_REFRESH_TOKEN: stored_refresh
CALENDLY_WEBHOOK_SECRET: webhook_signing_secret
Enter fullscreen mode Exit fullscreen mode

4. Validar assinaturas de webhook

Teste a validação da assinatura no Apidog:

pm.test('Webhook signature is valid', () => {
  const signature = pm.request.headers.get('Calendly-Webhook-Signature')
  pm.expect(signature).to.exist

  const payload = pm.request.body.raw
  const secret = pm.environment.get('CALENDLY_WEBHOOK_SECRET')

  // Verifique assinatura
  const valid = verifySignature(payload, signature, secret)
  pm.expect(valid).to.be.true
})
Enter fullscreen mode Exit fullscreen mode

Teste webhooks do Calendly com o Apidog — grátis.

Erros comuns e soluções

401 Não Autorizado

Causa: Token inválido ou expirado.

Solução:

  1. Confira se o token não expirou (expira em 2h)
  2. Use o refresh token para renovar o access token
  3. Certifique-se de enviar o header Authorization: Bearer {token}

403 Proibido

Causa: Escopo OAuth insuficiente.

Solução: Solicite a autorização com escopos corretos; o Calendly define escopos conforme aceito pelo usuário.

404 Não Encontrado

Causa: Recurso inexistente ou sem acesso.

Solução:

  1. Verifique a URI
  2. Confirme o acesso do usuário autenticado
  3. Garanta que os IDs sejam válidos

422 Entidade Não Processável

Causa: Erro de validação na requisição.

Solução: Veja a resposta detalhada:

{
  "title": "Validation Error",
  "message": "Invalid parameter: url must be a valid HTTPS URL"
}
Enter fullscreen mode Exit fullscreen mode

Alternativas e comparações

Recurso Calendly Acuity Cal.com Calendly
Camada gratuita Limitada Limitada Auto-hospedado grátis
Acesso à API
Webhooks
OAuth Chave de API Chave de API OAuth
Agendamento em equipe
Código aberto Não Não Sim Não

O Calendly oferece documentação de API e fluxo OAuth bem polidos. O Cal.com é uma alternativa open source com autenticação baseada em chave de API.

Casos de uso do mundo real

Integração com CRM de vendas:

  1. Criação de lead no Salesforce
  2. Notificação via Slack para vendas
  3. Adição à automação de marketing
  4. Registro de atividade em plataforma de sucesso do cliente

Plataforma de consultoria:

  1. Sincronização com sistema interno
  2. Geração de links Zoom
  3. Envio de questionário antes da reunião
  4. Criação de arquivo de caso ao finalizar

Agendamento de entrevistas:

  1. Atualização de ATS
  2. Notificação ao gerente de contratação
  3. Envio de convites de calendário
  4. Rastreamento de ausências

Conclusão

Resumo das ações práticas:

  • O Calendly usa OAuth 2.0 para autenticação de API
  • Acesse tipos de eventos e agendamentos via API
  • Webhooks notificam sobre eventos em tempo real
  • Sempre valide assinaturas de webhooks
  • Teste toda a integração com o Apidog antes de conectar a dados reais

Próximos passos:

  1. Crie um app OAuth no Calendly
  2. Implemente o fluxo OAuth
  3. Configure webhooks
  4. Teste com payloads simulados no Apidog
  5. Faça o deploy em produção

Teste os webhooks do Calendly com o Apidog — grátis.

Perguntas Frequentes

Preciso de um plano pago do Calendly para usar a API?

Não. A API está disponível em todos os planos, inclusive o gratuito (com limitações). Webhooks também estão disponíveis em todos os planos.

Qual a diferença entre webhooks de usuário e de organização?

Webhooks de usuário capturam eventos de um usuário. Webhooks de organização capturam eventos de toda a equipe. Integrações geralmente usam escopo de organização.

Como obtenho o segredo de assinatura do webhook?

Ao criar um webhook via API, a resposta inclui o campo signing_key. Armazene-o com segurança — ele serve para validar assinaturas dos webhooks.

Posso criar agendamentos via API?

Não. O Calendly não permite criar agendamentos via API; utilize a UI ou widgets. A API é somente leitura para agendamentos.

Como lido com conversão de fuso horário?

Todos os timestamps da API estão em UTC (ISO 8601). Converta para a hora local no seu app. O fuso horário do usuário está no recurso de usuário.

Qual o limite de requisições?

Limites de taxa não são publicados. Use padrões razoáveis e implemente backoff exponencial se necessário.

Consigo obter agendamentos históricos?

Sim. Utilize min_start_time e max_start_time para consultar eventos passados — não há limite de retroatividade.

Como testar o OAuth localmente?

Use um túnel como o ngrok para expor seu servidor local. Aponte a URI de redirecionamento para a URL do ngrok e complete o fluxo OAuth no navegador.

Top comments (0)