A API da Alibit permite que outros sistemas (ERP, site, app, chatbot) leiam e gravem a agenda de uma empresa:
profissionais, serviços, horários livres, clientes e agendamentos. Quando algo muda na agenda, avisamos o seu sistema
por webhooks.
URL basehttps://api.alibit.com.br/v1
FormatoJSON · UTF-8
AutenticaçãoX-Api-Key
Produção e sandbox
Existe uma única URL para testes e para produção. O que muda é a chave de API:
cada chave pertence a uma empresa, e a API só enxerga os dados dela.
Sandbox
Para desenvolver e testar
Uma empresa de testes criada para você, com nome Sandbox – sua empresa.
Já vem com dados fictícios: 2 profissionais, 5 serviços, 6 clientes e uma semana de agendamentos.
Pode criar, alterar e apagar à vontade: nada afeta clientes reais.
Mesmos limites e mesmas regras da produção (conflito de horário, expediente, validações).
Envie a chave no cabeçalho X-Api-Key de toda requisição. As chaves começam com agd_.
GET /v1/appointments HTTP/1.1
Host: api.alibit.com.br
X-Api-Key: agd_…
A chave age como o administrador da empresa que a criou e só enxerga os dados dessa empresa.
Use a chave só no servidor. Nunca a coloque em código que roda no navegador ou no celular do cliente.
Chave ausente, inválida ou revogada: resposta 401.
Plano sem API para integrações: resposta 403 com "code": "FEATURE_NOT_IN_PLAN".
Com a chave não é possível criar outras chaves, cadastrar webhooks nem redefinir senhas: isso é feito no painel.
Limites
Limite
Valor
Ao passar dele
Por minuto
120 requisições por chave
429 com o cabeçalho Retry-After (segundos)
Por mês
Depende do plano da empresa
429 com "code": "API_MONTHLY_QUOTA_EXCEEDED" até o dia 1º do mês seguinte
Quando o plano tem limite mensal, toda resposta traz X-Api-Quota-Limit (total do mês) e
X-Api-Quota-Remaining (quanto ainda resta). Planos sem limite mensal não enviam esses cabeçalhos.
Boa prática: em vez de consultar a agenda de tempos em tempos, use webhooks.
Você recebe cada mudança na hora e gasta muito menos requisições.
Erros
Os erros vêm em JSON. Alguns trazem um code estável para você tratar no seu sistema; em erros de validação, message é uma lista.
{
"statusCode": 409,
"message": "Já existe um agendamento neste horário",
"error": "Conflict"
}
Status
Quando acontece
400
Dados inválidos (campo faltando, formato errado, horário fora do expediente)
401
Chave ausente, inválida ou revogada
403
Empresa bloqueada (COMPANY_BLOCKED) ou recurso fora do plano (FEATURE_NOT_IN_PLAN)
404
Registro não existe ou pertence a outra empresa
409
Conflito: horário já ocupado, cliente com telefone ou e-mail repetido
429
Limite por minuto ou mensal atingido
5xx
Erro do nosso lado. Tente de novo com espera crescente (1 s, 2 s, 4 s…)
Datas e fusos
Data e hora sempre em ISO 8601, em UTC: 2026-10-09T13:00:00.000Z.
Ao enviar, você pode usar UTC (Z) ou informar o fuso: 2026-10-09T10:00:00-03:00 é o mesmo instante.
Converta para o fuso da empresa (America/Sao_Paulo) antes de mostrar ao usuário.
Datas sem hora, como o aniversário do cliente ou o dia consultado em horários livres, usam YYYY-MM-DD.
Horários de expediente usam HH:mm no fuso da empresa; dias da semana vão de 0 (domingo) a 6 (sábado).
Versionamento
Todas as rotas começam com /v1. Campos novos podem aparecer nas respostas a qualquer momento: ignore o que
não conhece. Mudanças que quebram compatibilidade vão para uma nova versão (/v2), com aviso prévio, e a
v1 continua funcionando.
Fluxo de agendamento
O caminho mais comum para marcar um horário pela sua integração:
Atenção: POST /v1/appointments marca na agenda do administrador dono da chave. Para listar a agenda de toda a
equipe, use GET /v1/team/appointments.
Webhooks
A empresa cadastra a URL do seu sistema no painel, em Integrações → Webhooks, e escolhe os eventos. A cada
mudança na agenda, enviamos um POST com JSON para essa URL.
Evento
Quando
Dados extras
appointment.created
Agendamento criado (painel, site de agendamento ou API)
Id da entrega: igual em todas as tentativas do mesmo evento
X-Webhook-Signature
t=<unix>,v1=<hex>: assinatura para conferir a origem
Entrega e reenvio
Responda com status 2xx em até 10 segundos. Qualquer outra resposta conta como falha.
Em caso de falha, reenviamos depois de 1 min, 5 min, 30 min, 2 h e 12 h.
Um evento pode chegar mais de uma vez: use o id do evento para descartar repetidos.
A URL precisa ser https e responder direto: não seguimos redirecionamentos.
Dica: responda 200 logo e processe o evento em segundo plano.
Conferindo a assinatura
Cada URL cadastrada tem um segredo (whsec_…) mostrado no painel. A assinatura v1 é um
HMAC-SHA256, com esse segredo, de "<t>.<corpo cru>". Calcule sobre o corpo exatamente
como chegou, antes de converter o JSON, e recuse mensagens com mais de 5 minutos.
import { createHmac, timingSafeEqual } from 'node:crypto';
export function webhookValido(corpoCru, cabecalho, segredo) {
const { t, v1 } = Object.fromEntries(cabecalho.split(',').map((p) => p.split('=')));
if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false; // mais de 5 min
const esperado = createHmac('sha256', segredo).update(`${t}.${corpoCru}`).digest('hex');
return v1?.length === esperado.length && timingSafeEqual(Buffer.from(v1), Buffer.from(esperado));
}
// Express: leia o corpo cru nesta rota
app.post('/webhooks/alibit', express.raw({ type: 'application/json' }), (req, res) => {
const corpo = req.body.toString('utf8');
if (!webhookValido(corpo, req.get('X-Webhook-Signature'), process.env.ALIBIT_WEBHOOK_SECRET)) {
return res.sendStatus(400);
}
res.sendStatus(200);
processarEvento(JSON.parse(corpo)); // em segundo plano
});
import hashlib, hmac, time
def webhook_valido(corpo_cru: bytes, cabecalho: str, segredo: str) -> bool:
partes = dict(p.split("=", 1) for p in cabecalho.split(","))
t, v1 = partes.get("t", "0"), partes.get("v1", "")
if abs(time.time() - int(t)) > 300: # mais de 5 min
return False
esperado = hmac.new(segredo.encode(), f"{t}.".encode() + corpo_cru, hashlib.sha256).hexdigest()
return hmac.compare_digest(v1, esperado)
# Flask
@app.post("/webhooks/alibit")
def receber():
if not webhook_valido(request.get_data(), request.headers["X-Webhook-Signature"], SEGREDO):
return "", 400
evento = request.get_json()
return "", 200
Todas exigem X-Api-Key e começam com https://api.alibit.com.br/v1. Campos marcados com
* são obrigatórios. Para testar as rotas no navegador, veja também a
referência interativa (Swagger).
Empresa
GET/companies/me
Dados da empresa da chave.
PATCH/team/company
Altera o nome da empresa. Corpo: name*.
Profissionais
GET/professionals?companyId=…
Profissionais que atendem. companyId*.
GET/professionals/{id}
Um profissional.
GET/professionals/{id}/working-hours
Expediente: lista de { weekday, startTime, endTime }.
PATCH/professionals/me
Altera o administrador dono da chave: name, phone, slotIntervalMinutes (15, 30 ou 60: intervalo entre horários livres).
PUT/professionals/me/working-hours
Substitui o expediente. Corpo: workingHours*: lista de { weekday 0–6, startTime "HH:mm", endTime "HH:mm" }.
Serviços
GET/services?professionalId=…
Serviços de um profissional. professionalId*.
GET/services/{id}
Um serviço.
POST/services
Cria um serviço do dono da chave. Corpo: name*, durationMinutes*, price* (em reais, ex.: 49.90).
PATCH/services/{id}
Altera name, durationMinutes ou price.
DELETE/services/{id}
Remove o serviço.
Clientes
GET/clients
Todos os clientes da empresa.
GET/clients/{id}
Um cliente.
GET/clients/birthdays?days=30
Aniversariantes dos próximos days dias (padrão 30).
POST/clients
Cria um cliente. Corpo: name*, phone*, email, birthDate (YYYY-MM-DD). Telefone e e-mail são únicos na empresa (409 se repetir).
PATCH/clients/{id}
Altera os mesmos campos. birthDate: null apaga a data.
DELETE/clients/{id}
Remove o cliente.
Agendamentos
GET/appointments/availability
Horários livres. Query: professionalId*, serviceId*, date* (YYYY-MM-DD), excludeAppointmentId (ao reagendar, ignora o próprio agendamento). Retorna { date, slots: [ISO…] }.
GET/appointments
Agenda do dono da chave, do mais recente ao mais antigo. Query opcional: from, to (ISO 8601), status, clientId.
GET/appointments/{id}
Um agendamento, com service e client.
POST/appointments
Cria. Corpo: serviceId*, clientId*, startTime* (ISO 8601), notes. Respeita expediente, bloqueios e conflitos (409).
PATCH/appointments/{id}/reschedule
Muda o horário. Corpo: startTime*. Guarda o horário original em originalStartTime e soma rescheduleCount.
PATCH/appointments/{id}/status
Corpo: status*: SCHEDULED, CONFIRMED, COMPLETED ou CANCELLED.
Status
Significado
SCHEDULED
Agendado
CONFIRMED
Confirmado pelo cliente
COMPLETED
Atendimento realizado
CANCELLED
Cancelado (libera o horário)
Bloqueios
Períodos em que o profissional não atende (folga, almoço, compromisso). Somem dos horários livres.
GET/blocks
Bloqueios do dono da chave. Query opcional: from, to.
POST/blocks
Corpo: startTime*, endTime* (ISO 8601), reason.
DELETE/blocks/{id}
Remove o bloqueio.
Equipe
Visão de toda a empresa, como o administrador vê no painel.
GET/team/professionals
Todos os profissionais, com papel, situação e se atendem clientes.
POST/team/professionals
Cria um profissional. Corpo: name*, email*, password* (senha provisória), phone, role (ADMIN ou PROFESSIONAL).
PATCH/team/professionals/{id}
Altera name, phone, role, active (bloquear/desbloquear) ou acceptsBookings (aparece para agendar).
GET/team/appointments
Agenda de toda a equipe. Query opcional: from, to, status, professionalId, rescheduled=true.
Suporte
Dúvidas, pedido de sandbox ou algo que não funciona como descrito aqui: escreva para
contato@alibit.com.br. Mande o horário, a rota chamada e o
status da resposta (nunca envie a chave completa).